diff --git a/AUTHORS b/AUTHORS
index bc76c72..856ac7b 100644
--- a/AUTHORS
+++ b/AUTHORS
@@ -1020,6 +1020,7 @@
 Collabora Limited <*@collabora.com>
 Comodo CA Limited
 Cosium <*@cosium.com>
+Duck Duck Go, Inc. <*@duckduckgo.com>
 Endless Mobile, Inc. <*@endlessm.com>
 Estimote, Inc. <*@estimote.com>
 Facebook, Inc. <*@fb.com>
diff --git a/DEPS b/DEPS
index 064b036..8320f7e 100644
--- a/DEPS
+++ b/DEPS
@@ -129,11 +129,11 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling Skia
   # and whatever else without interference from each other.
-  'skia_revision': '731eaa372a59ad3951ef2d461771b86e9e66c74e',
+  'skia_revision': '7656b2c18ce88e023f7911274535a3df34118817',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling V8
   # and whatever else without interference from each other.
-  'v8_revision': 'ce2242080787636827dd629ed5ee4e11a4368b9e',
+  'v8_revision': 'c796dae269b97c5a146a50e8bc710e3bbfae77f1',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling swarming_client
   # and whatever else without interference from each other.
@@ -141,11 +141,11 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling ANGLE
   # and whatever else without interference from each other.
-  'angle_revision': '90b1865e2a8b644bc3e200726de9e3a321ee5c18',
+  'angle_revision': '9f958344898ce0c4259d36c09c79e1a9de8ce47c',
   # 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': '3bb94905928d271476a7fb2c6a14912f2de94d9f',
+  'swiftshader_revision': 'eba396cc66d3d3d64859b769eb63a359b2d8c065',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling PDFium
   # and whatever else without interference from each other.
@@ -180,7 +180,7 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling NaCl
   # and whatever else without interference from each other.
-  'nacl_revision': '067afa275dcb4b3c6eca979ea5e3fe75073c4080',
+  'nacl_revision': '6abc006f6760ec49350cd45e8bccbff4809725ac',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling freetype
   # and whatever else without interference from each other.
@@ -252,7 +252,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.
-  'spv_tools_revision': 'e8c2d95ed45f0b839e7f04c05675921a5f624eff',
+  'spv_tools_revision': '7ce37d66a86d5869ed64e996c9ae694cd47796b8',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling feed
   # and whatever else without interference from each other.
@@ -805,7 +805,7 @@
 
   # Build tools for Chrome OS. Note: This depends on third_party/pyelftools.
   'src/third_party/chromite': {
-      'url': Var('chromium_git') + '/chromiumos/chromite.git' + '@' + 'de64cb214d81c5af372b16e1f51c415f851358f6',
+      'url': Var('chromium_git') + '/chromiumos/chromite.git' + '@' + '36589c6fccb25e55bdc289495fcebde2af994840',
       'condition': 'checkout_linux',
   },
 
@@ -899,7 +899,7 @@
   },
 
   'src/third_party/glslang/src':
-    Var('chromium_git') + '/external/github.com/KhronosGroup/glslang.git' + '@' + 'e06c7e9a515b716c731bda13f507546f107775d1',
+    Var('chromium_git') + '/external/github.com/KhronosGroup/glslang.git' + '@' + '0527c9db8148ce37442fa4a9c99a2a23ad50b0b7',
 
   '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'),
@@ -1172,7 +1172,7 @@
   },
 
   'src/third_party/perfetto':
-    Var('android_git') + '/platform/external/perfetto.git' + '@' +  '094a5527955836c210acb17ebffa744233963159',
+    Var('android_git') + '/platform/external/perfetto.git' + '@' +  'fb43b375bc15b88f115841f7ce336efa61df1ce6',
 
   'src/third_party/perl': {
       'url': Var('chromium_git') + '/chromium/deps/perl.git' + '@' + 'ac0d98b5cee6c024b0cffeb4f8f45b6fc5ccdb78',
@@ -1343,7 +1343,7 @@
     Var('chromium_git') + '/external/khronosgroup/webgl.git' + '@' + '688fbfe33779392aa210d67d4aa12cb012f112c2',
 
   'src/third_party/webrtc':
-    Var('webrtc_git') + '/src.git' + '@' + '59e875ce18bb24aa6d1ec5cdf128d7ef244fa37a',
+    Var('webrtc_git') + '/src.git' + '@' + 'e9d2b4efdd5dddaa3a476c0ac2a9cf9125b39929',
 
   'src/third_party/xdg-utils': {
       'url': Var('chromium_git') + '/chromium/deps/xdg-utils.git' + '@' + 'd80274d5869b17b8c9067a1022e4416ee7ed5e0d',
@@ -1384,7 +1384,7 @@
     Var('chromium_git') + '/v8/v8.git' + '@' +  Var('v8_revision'),
 
   'src-internal': {
-    'url': 'https://chrome-internal.googlesource.com/chrome/src-internal.git@40c6e15502942f8074065e6174ff40d8889bd439',
+    'url': 'https://chrome-internal.googlesource.com/chrome/src-internal.git@fa0935506a93a988c095b6e860c3cb9c3939e5da',
     'condition': 'checkout_src_internal',
   },
 
diff --git a/android_webview/glue/java/src/com/android/webview/chromium/MonochromeLibraryPreloader.java b/android_webview/glue/java/src/com/android/webview/chromium/MonochromeLibraryPreloader.java
index 0d0b824..b8e9f3cf 100644
--- a/android_webview/glue/java/src/com/android/webview/chromium/MonochromeLibraryPreloader.java
+++ b/android_webview/glue/java/src/com/android/webview/chromium/MonochromeLibraryPreloader.java
@@ -4,7 +4,7 @@
 
 package com.android.webview.chromium;
 
-import android.content.Context;
+import android.content.pm.ApplicationInfo;
 import android.webkit.WebViewFactory;
 
 import org.chromium.base.library_loader.NativeLibraryPreloader;
@@ -14,10 +14,9 @@
  * between Chrome and WebView.
  */
 public class MonochromeLibraryPreloader extends NativeLibraryPreloader {
-
     @Override
-    public int loadLibrary(Context context) {
-        return WebViewFactory.loadWebViewNativeLibraryFromPackage(context.getPackageName(),
-                getClass().getClassLoader());
+    public int loadLibrary(ApplicationInfo appInfo) {
+        return WebViewFactory.loadWebViewNativeLibraryFromPackage(
+                appInfo.packageName, getClass().getClassLoader());
     }
 }
diff --git a/ash/media/media_notification_controller.cc b/ash/media/media_notification_controller.cc
index 4f843be2..8ba97a5 100644
--- a/ash/media/media_notification_controller.cc
+++ b/ash/media/media_notification_controller.cc
@@ -69,7 +69,8 @@
 
   notifications_.emplace(
       std::piecewise_construct, std::forward_as_tuple(id),
-      std::forward_as_tuple(id, std::move(controller),
+      std::forward_as_tuple(id, session->source_name.value_or(std::string()),
+                            std::move(controller),
                             std::move(session->session_info)));
 }
 
diff --git a/ash/media/media_notification_controller_unittest.cc b/ash/media/media_notification_controller_unittest.cc
index f1808fd..c9836d7 100644
--- a/ash/media/media_notification_controller_unittest.cc
+++ b/ash/media/media_notification_controller_unittest.cc
@@ -13,6 +13,7 @@
 #include "ash/test/ash_test_base.h"
 #include "base/macros.h"
 #include "base/strings/utf_string_conversions.h"
+#include "base/test/metrics/histogram_tester.h"
 #include "base/test/scoped_feature_list.h"
 #include "base/unguessable_token.h"
 #include "services/media_session/public/mojom/audio_focus.mojom.h"
@@ -72,9 +73,17 @@
     return metadata;
   }
 
+  void ExpectHistogramSourceRecorded(MediaNotificationItem::Source source) {
+    histogram_tester_.ExpectUniqueSample(
+        MediaNotificationItem::kSourceHistogramName,
+        static_cast<base::HistogramBase::Sample>(source), 1);
+  }
+
  private:
   base::test::ScopedFeatureList scoped_feature_list_;
 
+  base::HistogramTester histogram_tester_;
+
   DISALLOW_COPY_AND_ASSIGN(MediaNotificationControllerTest);
 };
 
@@ -315,4 +324,84 @@
   ExpectNotificationCount(0);
 }
 
+TEST_F(MediaNotificationControllerTest, RecordHistogramSource_Unknown) {
+  base::UnguessableToken id = base::UnguessableToken::Create();
+
+  ExpectNotificationCount(0);
+
+  Shell::Get()->media_notification_controller()->OnFocusGained(
+      GetRequestStateWithId(id));
+
+  Shell::Get()
+      ->media_notification_controller()
+      ->GetItem(id.ToString())
+      ->MediaSessionMetadataChanged(BuildMediaMetadata());
+
+  ExpectNotificationCount(1);
+  ExpectHistogramSourceRecorded(MediaNotificationItem::Source::kUnknown);
+}
+
+TEST_F(MediaNotificationControllerTest, RecordHistogramSource_Web) {
+  base::UnguessableToken id = base::UnguessableToken::Create();
+
+  ExpectNotificationCount(0);
+
+  media_session::mojom::AudioFocusRequestStatePtr request =
+      GetRequestStateWithId(id);
+  request->source_name = "web";
+
+  Shell::Get()->media_notification_controller()->OnFocusGained(
+      std::move(request));
+
+  Shell::Get()
+      ->media_notification_controller()
+      ->GetItem(id.ToString())
+      ->MediaSessionMetadataChanged(BuildMediaMetadata());
+
+  ExpectNotificationCount(1);
+  ExpectHistogramSourceRecorded(MediaNotificationItem::Source::kWeb);
+}
+
+TEST_F(MediaNotificationControllerTest, RecordHistogramSource_Assistant) {
+  base::UnguessableToken id = base::UnguessableToken::Create();
+
+  ExpectNotificationCount(0);
+
+  media_session::mojom::AudioFocusRequestStatePtr request =
+      GetRequestStateWithId(id);
+  request->source_name = "assistant";
+
+  Shell::Get()->media_notification_controller()->OnFocusGained(
+      std::move(request));
+
+  Shell::Get()
+      ->media_notification_controller()
+      ->GetItem(id.ToString())
+      ->MediaSessionMetadataChanged(BuildMediaMetadata());
+
+  ExpectNotificationCount(1);
+  ExpectHistogramSourceRecorded(MediaNotificationItem::Source::kAssistant);
+}
+
+TEST_F(MediaNotificationControllerTest, RecordHistogramSource_Arc) {
+  base::UnguessableToken id = base::UnguessableToken::Create();
+
+  ExpectNotificationCount(0);
+
+  media_session::mojom::AudioFocusRequestStatePtr request =
+      GetRequestStateWithId(id);
+  request->source_name = "arc";
+
+  Shell::Get()->media_notification_controller()->OnFocusGained(
+      std::move(request));
+
+  Shell::Get()
+      ->media_notification_controller()
+      ->GetItem(id.ToString())
+      ->MediaSessionMetadataChanged(BuildMediaMetadata());
+
+  ExpectNotificationCount(1);
+  ExpectHistogramSourceRecorded(MediaNotificationItem::Source::kArc);
+}
+
 }  // namespace ash
diff --git a/ash/media/media_notification_item.cc b/ash/media/media_notification_item.cc
index 67c796f..ae61958d 100644
--- a/ash/media/media_notification_item.cc
+++ b/ash/media/media_notification_item.cc
@@ -31,17 +31,36 @@
 constexpr base::TimeDelta kDefaultSeekTime =
     base::TimeDelta::FromSeconds(media_session::mojom::kDefaultSeekTimeSeconds);
 
+MediaNotificationItem::Source GetSource(const std::string& name) {
+  if (name == "web")
+    return MediaNotificationItem::Source::kWeb;
+
+  if (name == "arc")
+    return MediaNotificationItem::Source::kArc;
+
+  if (name == "assistant")
+    return MediaNotificationItem::Source::kAssistant;
+
+  return MediaNotificationItem::Source::kUnknown;
+}
+
 }  // namespace
 
 // static
+const char MediaNotificationItem::kSourceHistogramName[] =
+    "Media.Notification.Source";
+
+// static
 const char MediaNotificationItem::kUserActionHistogramName[] =
     "Media.Notification.UserAction";
 
 MediaNotificationItem::MediaNotificationItem(
     const std::string& id,
+    const std::string& source_name,
     media_session::mojom::MediaControllerPtr controller,
     media_session::mojom::MediaSessionInfoPtr session_info)
     : id_(id),
+      source_(GetSource(source_name)),
       media_controller_ptr_(std::move(controller)),
       session_info_(std::move(session_info)) {
   if (media_controller_ptr_.is_bound()) {
@@ -180,6 +199,8 @@
 
   message_center::MessageCenter::Get()->AddNotification(
       std::move(notification));
+
+  UMA_HISTOGRAM_ENUMERATION(kSourceHistogramName, source_);
 }
 
 void MediaNotificationItem::HideNotification() {
diff --git a/ash/media/media_notification_item.h b/ash/media/media_notification_item.h
index 944e2d5..74a73022 100644
--- a/ash/media/media_notification_item.h
+++ b/ash/media/media_notification_item.h
@@ -29,7 +29,21 @@
   // The name of the histogram used when recording user actions.
   static const char kUserActionHistogramName[];
 
+  // The name of the histogram used when recording the source.
+  static const char kSourceHistogramName[];
+
+  // The source of the media session. This is used in metrics so new values must
+  // only be added to the end.
+  enum class Source {
+    kUnknown,
+    kWeb,
+    kAssistant,
+    kArc,
+    kMaxValue = kArc,
+  };
+
   MediaNotificationItem(const std::string& id,
+                        const std::string& source_name,
                         media_session::mojom::MediaControllerPtr controller,
                         media_session::mojom::MediaSessionInfoPtr session_info);
   ~MediaNotificationItem() override;
@@ -74,6 +88,9 @@
   // media session.
   const std::string id_;
 
+  // The source of the media session (e.g. arc, web).
+  const Source source_;
+
   media_session::mojom::MediaControllerPtr media_controller_ptr_;
 
   media_session::mojom::MediaSessionInfoPtr session_info_;
diff --git a/ash/public/interfaces/shell_test_api.test-mojom b/ash/public/interfaces/shell_test_api.test-mojom
index 4b17732..4dbd33d1 100644
--- a/ash/public/interfaces/shell_test_api.test-mojom
+++ b/ash/public/interfaces/shell_test_api.test-mojom
@@ -27,6 +27,9 @@
   // Enters or exits overview mode.
   ToggleOverviewMode() => ();
 
+  // Returns true if it is in overview selecting mode.
+  IsOverviewSelecting() => (bool is_selecting);
+
   // Used to emulate display change when run in a desktop environment instead
   // of on a device.
   AddRemoveDisplay();
@@ -38,4 +41,8 @@
   // longer holding pointer events. See
   // |aura::WindowTreeHost::holding_pointer_moves_| for details.
   WaitForNoPointerHoldLock() => ();
+
+  // Runs the callback when the compositor of the primary display has presented
+  // a frame on screen.
+  WaitForNextFrame() => ();
 };
diff --git a/ash/public/interfaces/user_info.mojom b/ash/public/interfaces/user_info.mojom
index c40bb51..e74b5b3 100644
--- a/ash/public/interfaces/user_info.mojom
+++ b/ash/public/interfaces/user_info.mojom
@@ -64,4 +64,6 @@
   bool is_device_owner;
   // True if the user has a gaia account.
   bool has_gaia_account;
+  // True if user is managed.
+  bool is_managed;
 };
diff --git a/ash/session/session_controller.cc b/ash/session/session_controller.cc
index efa4f2f..3f0d8daf 100644
--- a/ash/session/session_controller.cc
+++ b/ash/session/session_controller.cc
@@ -235,6 +235,13 @@
   return GetUserSession(0)->user_info->is_new_profile;
 }
 
+bool SessionController::IsUserManaged() const {
+  if (!IsActiveUserSessionStarted())
+    return false;
+
+  return GetUserSession(0)->user_info->is_managed;
+}
+
 void SessionController::LockScreen() {
   if (client_)
     client_->RequestLockScreen();
diff --git a/ash/session/session_controller.h b/ash/session/session_controller.h
index e530938f..5d7009b 100644
--- a/ash/session/session_controller.h
+++ b/ash/session/session_controller.h
@@ -135,6 +135,12 @@
   // device (i.e. first time login on the device).
   bool IsUserFirstLogin() const;
 
+  // Returns true if the current user is managed, and false otherwise.
+  // For public sessions, kiosk app and arc kiosk app accounts returns true.
+  // NOTE: Returns false if there is no profile (example: login screen in
+  // ChromeOS).
+  bool IsUserManaged() const;
+
   // Locks the screen. The locking happens asynchronously.
   void LockScreen();
 
diff --git a/ash/shell_test_api.cc b/ash/shell_test_api.cc
index be6517e..3204e01 100644
--- a/ash/shell_test_api.cc
+++ b/ash/shell_test_api.cc
@@ -169,6 +169,10 @@
   std::move(cb).Run();
 }
 
+void ShellTestApi::IsOverviewSelecting(IsOverviewSelectingCallback callback) {
+  std::move(callback).Run(shell_->overview_controller()->IsSelecting());
+}
+
 void ShellTestApi::AddRemoveDisplay() {
   shell_->display_manager()->AddRemoveDisplay();
 }
@@ -186,4 +190,16 @@
   std::move(callback).Run();
 }
 
+void ShellTestApi::WaitForNextFrame(WaitForNextFrameCallback callback) {
+  Shell::GetPrimaryRootWindowController()
+      ->GetHost()
+      ->compositor()
+      ->RequestPresentationTimeForNextFrame(base::BindOnce(
+          [](WaitForNextFrameCallback callback,
+             const gfx::PresentationFeedback& feedback) {
+            std::move(callback).Run();
+          },
+          std::move(callback)));
+}
+
 }  // namespace ash
diff --git a/ash/shell_test_api.h b/ash/shell_test_api.h
index 93e62afa..c883c4d 100644
--- a/ash/shell_test_api.h
+++ b/ash/shell_test_api.h
@@ -61,10 +61,12 @@
                              SnapWindowInSplitViewCallback cb) override;
   void ToggleFullscreen(ToggleFullscreenCallback cb) override;
   void ToggleOverviewMode(ToggleOverviewModeCallback cb) override;
+  void IsOverviewSelecting(IsOverviewSelectingCallback callback) override;
   void AddRemoveDisplay() override;
   void SetMinFlingVelocity(float velocity) override;
   void WaitForNoPointerHoldLock(
       WaitForNoPointerHoldLockCallback callback) override;
+  void WaitForNextFrame(WaitForNextFrameCallback callback) override;
 
  private:
   Shell* shell_;  // not owned
diff --git a/ash/system/unified/unified_system_info_view.cc b/ash/system/unified/unified_system_info_view.cc
index 5d750ad..efe5893 100644
--- a/ash/system/unified/unified_system_info_view.cc
+++ b/ash/system/unified/unified_system_info_view.cc
@@ -7,6 +7,7 @@
 #include "ash/public/cpp/ash_view_ids.h"
 #include "ash/resources/vector_icons/vector_icons.h"
 #include "ash/session/session_controller.h"
+#include "ash/session/session_observer.h"
 #include "ash/shell.h"
 #include "ash/strings/grit/ash_strings.h"
 #include "ash/system/enterprise/enterprise_domain_observer.h"
@@ -265,7 +266,8 @@
 // by observing EnterpriseDomainModel.
 class EnterpriseManagedView : public ManagedStateView,
                               public views::ButtonListener,
-                              public EnterpriseDomainObserver {
+                              public EnterpriseDomainObserver,
+                              public SessionObserver {
  public:
   explicit EnterpriseManagedView(UnifiedSystemTrayController* controller);
   ~EnterpriseManagedView() override;
@@ -276,6 +278,9 @@
   // EnterpriseDomainObserver:
   void OnEnterpriseDomainChanged() override;
 
+  // SessionObserver:
+  void OnLoginStatusChanged(LoginStatus status) override;
+
  private:
   void Update();
 
@@ -293,11 +298,13 @@
   DCHECK(Shell::Get());
   set_id(VIEW_ID_TRAY_ENTERPRISE);
   Shell::Get()->system_tray_model()->enterprise_domain()->AddObserver(this);
+  Shell::Get()->session_controller()->AddObserver(this);
   Update();
 }
 
 EnterpriseManagedView::~EnterpriseManagedView() {
   Shell::Get()->system_tray_model()->enterprise_domain()->RemoveObserver(this);
+  Shell::Get()->session_controller()->RemoveObserver(this);
 }
 
 void EnterpriseManagedView::ButtonPressed(views::Button* sender,
@@ -309,10 +316,16 @@
   Update();
 }
 
+void EnterpriseManagedView::OnLoginStatusChanged(LoginStatus status) {
+  Update();
+}
+
 void EnterpriseManagedView::Update() {
   EnterpriseDomainModel* model =
       Shell::Get()->system_tray_model()->enterprise_domain();
-  SetVisible(model->active_directory_managed() ||
+  SessionController* session_controller = Shell::Get()->session_controller();
+  SetVisible(session_controller->IsUserManaged() ||
+             model->active_directory_managed() ||
              !model->enterprise_display_domain().empty());
 
   if (model->active_directory_managed()) {
diff --git a/base/BUILD.gn b/base/BUILD.gn
index 215417e..fadacd7 100644
--- a/base/BUILD.gn
+++ b/base/BUILD.gn
@@ -804,6 +804,7 @@
     "task/task_features.cc",
     "task/task_features.h",
     "task/task_observer.h",
+    "task/task_scheduler/can_schedule_sequence_observer.h",
     "task/task_scheduler/delayed_task_manager.cc",
     "task/task_scheduler/delayed_task_manager.h",
     "task/task_scheduler/environment_config.cc",
@@ -2580,7 +2581,6 @@
     "task/sequence_manager/work_deduplicator_unittest.cc",
     "task/sequence_manager/work_queue_sets_unittest.cc",
     "task/sequence_manager/work_queue_unittest.cc",
-    "task/task_scheduler/can_run_policy_test.h",
     "task/task_scheduler/delayed_task_manager_unittest.cc",
     "task/task_scheduler/priority_queue_unittest.cc",
     "task/task_scheduler/scheduler_lock_unittest.cc",
diff --git a/base/android/java/src/org/chromium/base/library_loader/LibraryLoader.java b/base/android/java/src/org/chromium/base/library_loader/LibraryLoader.java
index 599428c..398e081 100644
--- a/base/android/java/src/org/chromium/base/library_loader/LibraryLoader.java
+++ b/base/android/java/src/org/chromium/base/library_loader/LibraryLoader.java
@@ -8,6 +8,7 @@
 
 import android.annotation.SuppressLint;
 import android.content.Context;
+import android.content.pm.ApplicationInfo;
 import android.os.Build;
 import android.os.Build.VERSION_CODES;
 import android.os.StrictMode;
@@ -117,6 +118,10 @@
     // One-way switch becomes true when the libraries are loaded.
     private boolean mLoaded;
 
+    // Similar to |mLoaded| but is limited case of being loaded in app zygote.
+    // This is exposed to clients.
+    private boolean mLoadedByZygote;
+
     // One-way switch becomes true when the Java command line is switched to
     // native.
     private boolean mCommandLineSwitched;
@@ -184,6 +189,13 @@
     private LibraryLoader() {}
 
     /**
+     * Return if library is already loaded successfully by the zygote.
+     */
+    public boolean isLoadedByZygote() {
+        return mLoadedByZygote;
+    }
+
+    /**
      *  This method blocks until the library is fully loaded and initialized.
      *
      * @param processType the process the shared library is loaded in.
@@ -194,7 +206,8 @@
                 // Already initialized, nothing to do.
                 return;
             }
-            loadAlreadyLocked(ContextUtils.getApplicationContext());
+            loadAlreadyLocked(ContextUtils.getApplicationContext().getApplicationInfo(),
+                    false /* inZygote */);
             initializeAlreadyLocked(processType);
         }
     }
@@ -215,17 +228,17 @@
     public void preloadNowOverrideApplicationContext(Context appContext) {
         synchronized (mLock) {
             if (!useCrazyLinker()) {
-                preloadAlreadyLocked(appContext);
+                preloadAlreadyLocked(appContext.getApplicationInfo());
             }
         }
     }
 
-    private void preloadAlreadyLocked(Context appContext) {
+    private void preloadAlreadyLocked(ApplicationInfo appInfo) {
         try (TraceEvent te = TraceEvent.scoped("LibraryLoader.preloadAlreadyLocked")) {
             // Preloader uses system linker, we shouldn't preload if Chromium linker is used.
             assert !useCrazyLinker();
             if (mLibraryPreloader != null && !mLibraryPreloaderCalled) {
-                mLibraryPreloaderStatus = mLibraryPreloader.loadLibrary(appContext);
+                mLibraryPreloaderStatus = mLibraryPreloader.loadLibrary(appInfo);
                 mLibraryPreloaderCalled = true;
             }
         }
@@ -264,7 +277,15 @@
             if (mLoaded && appContext != ContextUtils.getApplicationContext()) {
                 throw new IllegalStateException("Attempt to load again from alternate context.");
             }
-            loadAlreadyLocked(appContext);
+            loadAlreadyLocked(appContext.getApplicationInfo(), false /* inZygote */);
+        }
+    }
+
+    public void loadNowInZygote(ApplicationInfo appInfo) throws ProcessInitException {
+        synchronized (mLock) {
+            assert !mLoaded;
+            loadAlreadyLocked(appInfo, true /* inZygote */);
+            mLoadedByZygote = true;
         }
     }
 
@@ -422,31 +443,31 @@
     // Experience shows that on some devices, the system sometimes fails to extract native libraries
     // at installation or update time from the APK. This function will extract the library and
     // return the extracted file path.
-    static String getExtractedLibraryPath(Context appContext, String libName) {
+    static String getExtractedLibraryPath(ApplicationInfo appInfo, String libName) {
         assert PLATFORM_REQUIRES_NATIVE_FALLBACK_EXTRACTION;
         Log.w(TAG, "Failed to load libName %s, attempting fallback extraction then trying again",
                 libName);
         String libraryEntry = LibraryLoader.makeLibraryPathInZipFile(libName, false, false);
-        return extractFileIfStale(appContext, libraryEntry, makeLibraryDirAndSetPermission());
+        return extractFileIfStale(appInfo, libraryEntry, makeLibraryDirAndSetPermission());
     }
 
     // Invoke either Linker.loadLibrary(...), System.loadLibrary(...) or System.load(...),
     // triggering JNI_OnLoad in native code.
     // TODO(crbug.com/635567): Fix this properly.
     @SuppressLint({"DefaultLocale", "UnsafeDynamicallyLoadedCode"})
-    private void loadAlreadyLocked(Context appContext) throws ProcessInitException {
+    private void loadAlreadyLocked(ApplicationInfo appInfo, boolean inZygote)
+            throws ProcessInitException {
         try (TraceEvent te = TraceEvent.scoped("LibraryLoader.loadAlreadyLocked")) {
             if (!mLoaded) {
                 assert !mInitialized;
 
                 long startTime = SystemClock.uptimeMillis();
 
-                if (useCrazyLinker()) {
+                if (useCrazyLinker() && !inZygote) {
                     // Load libraries using the Chromium linker.
                     Linker linker = Linker.getInstance();
 
-                    String apkFilePath =
-                            isInZipFile() ? appContext.getApplicationInfo().sourceDir : null;
+                    String apkFilePath = isInZipFile() ? appInfo.sourceDir : null;
                     linker.prepareLibraryLoad(apkFilePath);
 
                     for (String library : NativeLibraries.LIBRARIES) {
@@ -475,7 +496,7 @@
                             if (!isInZipFile()
                                     && PLATFORM_REQUIRES_NATIVE_FALLBACK_EXTRACTION) {
                                 loadLibraryWithCustomLinkerAlreadyLocked(
-                                        linker, null, getExtractedLibraryPath(appContext, library));
+                                        linker, null, getExtractedLibraryPath(appInfo, library));
                                 incrementRelinkerCountHitHistogram();
                             } else {
                                 Log.e(TAG, "Unable to load library: " + library);
@@ -487,7 +508,7 @@
                     linker.finishLibraryLoad();
                 } else {
                     setEnvForNative();
-                    preloadAlreadyLocked(appContext);
+                    preloadAlreadyLocked(appInfo);
 
                     // If the libraries are located in the zip file, assert that the device API
                     // level is M or higher. On devices lower than M, the libraries should
@@ -504,7 +525,7 @@
                             } else {
                                 // Load directly from the APK.
                                 boolean is64Bit = ApiHelperForM.isProcess64Bit();
-                                String zipFilePath = appContext.getApplicationInfo().sourceDir;
+                                String zipFilePath = appInfo.sourceDir;
                                 // In API level 23 and above, it’s possible to open a .so file
                                 // directly from the APK of the path form
                                 // "my_zip_file.zip!/libs/libstuff.so". See:
@@ -744,10 +765,10 @@
     // This function manually extract libraries as a fallback.
     @SuppressLint({"SetWorldReadable"})
     private static String extractFileIfStale(
-            Context appContext, String pathWithinApk, File destDir) {
+            ApplicationInfo appInfo, String pathWithinApk, File destDir) {
         assert PLATFORM_REQUIRES_NATIVE_FALLBACK_EXTRACTION;
 
-        String apkPath = appContext.getApplicationInfo().sourceDir;
+        String apkPath = appInfo.sourceDir;
         String fileName =
                 (new File(pathWithinApk)).getName() + BuildInfo.getInstance().extractedFileSuffix;
         File libraryFile = new File(destDir, fileName);
diff --git a/base/android/java/src/org/chromium/base/library_loader/Linker.java b/base/android/java/src/org/chromium/base/library_loader/Linker.java
index 0d401ab..68c42ef 100644
--- a/base/android/java/src/org/chromium/base/library_loader/Linker.java
+++ b/base/android/java/src/org/chromium/base/library_loader/Linker.java
@@ -434,7 +434,8 @@
         } catch (UnsatisfiedLinkError e) {
             if (LibraryLoader.PLATFORM_REQUIRES_NATIVE_FALLBACK_EXTRACTION) {
                 System.load(LibraryLoader.getExtractedLibraryPath(
-                        ContextUtils.getApplicationContext(), LINKER_JNI_LIBRARY));
+                        ContextUtils.getApplicationContext().getApplicationInfo(),
+                        LINKER_JNI_LIBRARY));
                 LibraryLoader.incrementRelinkerCountHitHistogram();
             }
         }
diff --git a/base/android/java/src/org/chromium/base/library_loader/NativeLibraryPreloader.java b/base/android/java/src/org/chromium/base/library_loader/NativeLibraryPreloader.java
index 6f8008d..56f2cbc 100644
--- a/base/android/java/src/org/chromium/base/library_loader/NativeLibraryPreloader.java
+++ b/base/android/java/src/org/chromium/base/library_loader/NativeLibraryPreloader.java
@@ -4,7 +4,7 @@
 
 package org.chromium.base.library_loader;
 
-import android.content.Context;
+import android.content.pm.ApplicationInfo;
 
 /**
  * This is interface to preload the native library before calling System.loadLibrary.
@@ -16,5 +16,5 @@
  * only subclass (MonochromeLibraryPreloader) is doing.
  */
 public abstract class NativeLibraryPreloader {
-    public abstract int loadLibrary(Context context);
+    public abstract int loadLibrary(ApplicationInfo appInfo);
 }
diff --git a/base/sequence_checker.h b/base/sequence_checker.h
index 81e74a6..e2ce10f 100644
--- a/base/sequence_checker.h
+++ b/base/sequence_checker.h
@@ -55,7 +55,7 @@
   DCHECK((name).CalledOnValidSequence())
 #define DETACH_FROM_SEQUENCE(name) (name).DetachFromSequence()
 #else  // DCHECK_IS_ON()
-#if __OBJC__ && defined(OS_IOS) && !__has_feature(objc_cxx_static_assert)
+#if __OBJC__ && defined(OS_IOS) && !HAS_FEATURE(objc_cxx_static_assert)
 // TODO(thakis): Remove this branch once Xcode's clang has clang r356148.
 #define SEQUENCE_CHECKER(name)
 #else
diff --git a/base/task/task_scheduler/can_run_policy_test.h b/base/task/task_scheduler/can_run_policy_test.h
deleted file mode 100644
index 7a4fe2e..0000000
--- a/base/task/task_scheduler/can_run_policy_test.h
+++ /dev/null
@@ -1,134 +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.
-
-#ifndef BASE_TASK_TASK_SCHEDULER_CAN_RUN_POLICY_TEST_H_
-#define BASE_TASK_TASK_SCHEDULER_CAN_RUN_POLICY_TEST_H_
-
-#include "base/synchronization/atomic_flag.h"
-#include "base/synchronization/waitable_event.h"
-#include "base/task/task_scheduler/task_tracker.h"
-#include "base/task/task_scheduler/test_utils.h"
-#include "base/task_runner.h"
-#include "base/test/bind_test_util.h"
-#include "base/test/test_timeouts.h"
-#include "base/threading/platform_thread.h"
-
-namespace base {
-namespace internal {
-namespace test {
-
-// Verify that tasks only run when allowed by the CanRunPolicy. |target| is the
-// object on which DidUpdateCanRunPolicy() must be called after updating the
-// CanRunPolicy in |task_tracker|. |create_task_runner| is a function that
-// receives a TaskPriority and returns a TaskRunner. |task_tracker| is the
-// TaskTracker.
-template <typename Target, typename CreateTaskRunner>
-void TestCanRunPolicyBasic(Target* target,
-                           CreateTaskRunner create_task_runner,
-                           TaskTracker* task_tracker) {
-  AtomicFlag foreground_can_run;
-  WaitableEvent foreground_did_run;
-  AtomicFlag best_effort_can_run;
-  WaitableEvent best_effort_did_run;
-
-  task_tracker->SetCanRunPolicy(CanRunPolicy::kNone);
-  target->DidUpdateCanRunPolicy();
-
-  const auto user_visible_task_runner =
-      create_task_runner(TaskPriority::USER_VISIBLE);
-  user_visible_task_runner->PostTask(FROM_HERE, BindLambdaForTesting([&]() {
-                                       EXPECT_TRUE(foreground_can_run.IsSet());
-                                       foreground_did_run.Signal();
-                                     }));
-  const auto best_effort_task_runner =
-      create_task_runner(TaskPriority::BEST_EFFORT);
-  best_effort_task_runner->PostTask(FROM_HERE, BindLambdaForTesting([&]() {
-                                      EXPECT_TRUE(best_effort_can_run.IsSet());
-                                      best_effort_did_run.Signal();
-                                    }));
-
-  PlatformThread::Sleep(TestTimeouts::tiny_timeout());
-
-  foreground_can_run.Set();
-  task_tracker->SetCanRunPolicy(CanRunPolicy::kForegroundOnly);
-  target->DidUpdateCanRunPolicy();
-  foreground_did_run.Wait();
-
-  PlatformThread::Sleep(TestTimeouts::tiny_timeout());
-
-  best_effort_can_run.Set();
-  task_tracker->SetCanRunPolicy(CanRunPolicy::kAll);
-  target->DidUpdateCanRunPolicy();
-  best_effort_did_run.Wait();
-}
-
-// Verify that if a task was allowed to run by the CanRunPolicy when it was
-// posted, but the CanRunPolicy is updated to disallow it from running before it
-// starts running, it doesn't run. |target| is the object on which
-// DidUpdateCanRunPolicy() must be called after updating the CanRunPolicy in
-// |task_tracker|. |create_task_runner| is a function that receives a
-// TaskPriority and returns a *Sequenced*TaskRunner. |task_tracker| is the
-// TaskTracker.
-template <typename Target, typename CreateTaskRunner>
-void TestCanRunPolicyChangedBeforeRun(Target* target,
-                                      CreateTaskRunner create_task_runner,
-                                      TaskTracker* task_tracker) {
-  constexpr struct {
-    // Descriptor for the test case.
-    const char* descriptor;
-    // Task priority being tested.
-    TaskPriority priority;
-    // Policy that disallows running tasks with |priority|.
-    CanRunPolicy disallow_policy;
-    // Policy that allows running tasks with |priority|.
-    CanRunPolicy allow_policy;
-  } kTestCases[] = {
-      {"BestEffort/kNone/kAll", TaskPriority::BEST_EFFORT, CanRunPolicy::kNone,
-       CanRunPolicy::kAll},
-      {"BestEffort/kForegroundOnly/kAll", TaskPriority::BEST_EFFORT,
-       CanRunPolicy::kForegroundOnly, CanRunPolicy::kAll},
-      {"UserVisible/kNone/kForegroundOnly", TaskPriority::USER_VISIBLE,
-       CanRunPolicy::kNone, CanRunPolicy::kForegroundOnly},
-      {"UserVisible/kNone/kAll", TaskPriority::USER_VISIBLE,
-       CanRunPolicy::kNone, CanRunPolicy::kAll}};
-
-  for (auto& test_case : kTestCases) {
-    SCOPED_TRACE(test_case.descriptor);
-
-    WaitableEvent first_task_started;
-    WaitableEvent first_task_blocked;
-    AtomicFlag second_task_can_run;
-
-    task_tracker->SetCanRunPolicy(test_case.allow_policy);
-    target->DidUpdateCanRunPolicy();
-
-    const auto task_runner = create_task_runner(test_case.priority);
-    task_runner->PostTask(
-        FROM_HERE, BindLambdaForTesting([&]() {
-          first_task_started.Signal();
-          test::WaitWithoutBlockingObserver(&first_task_blocked);
-        }));
-    task_runner->PostTask(FROM_HERE, BindLambdaForTesting([&]() {
-                            EXPECT_TRUE(second_task_can_run.IsSet());
-                          }));
-
-    first_task_started.Wait();
-    task_tracker->SetCanRunPolicy(test_case.disallow_policy);
-    target->DidUpdateCanRunPolicy();
-    first_task_blocked.Signal();
-
-    PlatformThread::Sleep(TestTimeouts::tiny_timeout());
-
-    second_task_can_run.Set();
-    task_tracker->SetCanRunPolicy(test_case.allow_policy);
-    target->DidUpdateCanRunPolicy();
-    task_tracker->FlushForTesting();
-  }
-}
-
-}  // namespace test
-}  // namespace internal
-}  // namespace base
-
-#endif  // BASE_TASK_TASK_SCHEDULER_CAN_RUN_POLICY_TEST_H_
diff --git a/base/task/task_scheduler/can_schedule_sequence_observer.h b/base/task/task_scheduler/can_schedule_sequence_observer.h
new file mode 100644
index 0000000..50284a1
--- /dev/null
+++ b/base/task/task_scheduler/can_schedule_sequence_observer.h
@@ -0,0 +1,27 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef BASE_TASK_TASK_SCHEDULER_CAN_SCHEDULE_SEQUENCE_OBSERVER_H_
+#define BASE_TASK_TASK_SCHEDULER_CAN_SCHEDULE_SEQUENCE_OBSERVER_H_
+
+#include "base/task/task_scheduler/sequence.h"
+
+namespace base {
+namespace internal {
+
+class CanScheduleSequenceObserver {
+ public:
+  // Called when |sequence| can be scheduled. It is expected that
+  // TaskTracker::RunNextTask() will be called with |sequence| as argument after
+  // this is called.
+  virtual void OnCanScheduleSequence(scoped_refptr<Sequence> sequence) = 0;
+
+ protected:
+  virtual ~CanScheduleSequenceObserver() = default;
+};
+
+}  // namespace internal
+}  // namespace base
+
+#endif  // BASE_TASK_TASK_SCHEDULER_CAN_SCHEDULE_SEQUENCE_OBSERVER_H_
diff --git a/base/task/task_scheduler/platform_native_worker_pool.cc b/base/task/task_scheduler/platform_native_worker_pool.cc
index a9fd60f6..d32f278b 100644
--- a/base/task/task_scheduler/platform_native_worker_pool.cc
+++ b/base/task/task_scheduler/platform_native_worker_pool.cc
@@ -80,7 +80,7 @@
   scoped_refptr<Sequence> sequence = GetWork();
   DCHECK(sequence);
 
-  sequence = task_tracker_->RunAndPopNextTask(std::move(sequence));
+  sequence = task_tracker_->RunAndPopNextTask(std::move(sequence), this);
 
   if (sequence) {
     ScopedWorkersExecutor workers_executor(this);
@@ -103,11 +103,6 @@
   // PriorityQueue after RemoveSequence().
   if (priority_queue_.IsEmpty())
     return nullptr;
-
-  // Enforce the CanRunPolicy.
-  const TaskPriority priority = priority_queue_.PeekSortKey().priority();
-  if (!task_tracker_->CanRunPriority(priority))
-    return nullptr;
   return priority_queue_.PopSequence();
 }
 
@@ -130,10 +125,7 @@
     return;
   // Ensure that there is at least one pending threadpool work per Sequence in
   // the PriorityQueue.
-  const size_t desired_num_pending_threadpool_work =
-      GetNumQueuedCanRunBestEffortSequences() +
-      GetNumQueuedCanRunForegroundSequences();
-
+  const size_t desired_num_pending_threadpool_work = priority_queue_.Size();
   if (desired_num_pending_threadpool_work > num_pending_threadpool_work_) {
     static_cast<ScopedWorkersExecutor*>(executor)
         ->set_num_threadpool_work_to_submit(
@@ -157,11 +149,5 @@
   // number of worker threads created.
 }
 
-void PlatformNativeWorkerPool::DidUpdateCanRunPolicy() {
-  ScopedWorkersExecutor executor(this);
-  AutoSchedulerLock auto_lock(lock_);
-  EnsureEnoughWorkersLockRequired(&executor);
-}
-
 }  // namespace internal
 }  // namespace base
diff --git a/base/task/task_scheduler/platform_native_worker_pool.h b/base/task/task_scheduler/platform_native_worker_pool.h
index f82b132..79e680a4 100644
--- a/base/task/task_scheduler/platform_native_worker_pool.h
+++ b/base/task/task_scheduler/platform_native_worker_pool.h
@@ -26,7 +26,6 @@
   void JoinForTesting() override;
   size_t GetMaxConcurrentNonBlockedTasksDeprecated() const override;
   void ReportHeartbeatMetrics() const override;
-  void DidUpdateCanRunPolicy() override;
 
  protected:
   PlatformNativeWorkerPool(TrackedRef<TaskTracker> task_tracker,
diff --git a/base/task/task_scheduler/scheduler_single_thread_task_runner_manager.cc b/base/task/task_scheduler/scheduler_single_thread_task_runner_manager.cc
index b10a2ed..c5cb647 100644
--- a/base/task/task_scheduler/scheduler_single_thread_task_runner_manager.cc
+++ b/base/task/task_scheduler/scheduler_single_thread_task_runner_manager.cc
@@ -79,17 +79,22 @@
 class SchedulerWorkerDelegate : public SchedulerWorker::Delegate {
  public:
   SchedulerWorkerDelegate(const std::string& thread_name,
-                          SchedulerWorker::ThreadLabel thread_label,
-                          TrackedRef<TaskTracker> task_tracker)
-      : thread_name_(thread_name),
-        thread_label_(thread_label),
-        task_tracker_(std::move(task_tracker)) {}
+                          SchedulerWorker::ThreadLabel thread_label)
+      : thread_name_(thread_name), thread_label_(thread_label) {}
 
   void set_worker(SchedulerWorker* worker) {
     DCHECK(!worker_);
     worker_ = worker;
   }
 
+  // SchedulerWorker::Delegate:
+  void OnCanScheduleSequence(scoped_refptr<Sequence> sequence) override {
+    DCHECK(worker_);
+    ReEnqueueSequence(
+        SequenceAndTransaction::FromSequence(std::move(sequence)));
+    worker_->WakeUp();
+  }
+
   SchedulerWorker::ThreadLabel GetThreadLabel() const final {
     return thread_label_;
   }
@@ -101,35 +106,26 @@
 
   scoped_refptr<Sequence> GetWork(SchedulerWorker* worker) override {
     AutoSchedulerLock auto_lock(lock_);
-    auto sequence = GetWorkLockRequired(worker);
-    if (sequence == nullptr) {
-      // The worker will sleep after this returns nullptr.
-      worker_awake_ = false;
-    }
-    return sequence;
+    return priority_queue_.IsEmpty() ? nullptr : priority_queue_.PopSequence();
   }
 
   void DidRunTask(scoped_refptr<Sequence> sequence) override {
     if (sequence) {
-      EnqueueSequence(
+      ReEnqueueSequence(
           SequenceAndTransaction::FromSequence(std::move(sequence)));
     }
   }
 
-  TimeDelta GetSleepTimeout() override { return TimeDelta::Max(); }
-
-  void PostTaskNow(scoped_refptr<Sequence> sequence, Task task) {
-    auto sequence_and_transaction =
-        SequenceAndTransaction::FromSequence(std::move(sequence));
-    const bool task_source_should_be_queued =
-        sequence_and_transaction.transaction.PushTask(std::move(task));
-    if (task_source_should_be_queued) {
-      bool should_wakeup = EnqueueSequence(std::move(sequence_and_transaction));
-      if (should_wakeup)
-        worker_->WakeUp();
-    }
+  void ReEnqueueSequence(SequenceAndTransaction sequence_and_transaction) {
+    const SequenceSortKey sequence_sort_key =
+        sequence_and_transaction.transaction.GetSortKey();
+    AutoSchedulerLock auto_lock(lock_);
+    priority_queue_.Push(std::move(sequence_and_transaction.sequence),
+                         sequence_sort_key);
   }
 
+  TimeDelta GetSleepTimeout() override { return TimeDelta::Max(); }
+
   bool RunsTasksInCurrentSequence() {
     // We check the thread ref instead of the sequence for the benefit of COM
     // callbacks which may execute without a sequence context.
@@ -138,69 +134,22 @@
 
   void OnMainExit(SchedulerWorker* /* worker */) override {}
 
-  void DidUpdateCanRunPolicy() {
-    bool should_wakeup = false;
-    {
-      AutoSchedulerLock auto_lock(lock_);
-      if (!worker_awake_ && CanRunNextSequence()) {
-        should_wakeup = true;
-        worker_awake_ = true;
-      }
-    }
-    if (should_wakeup)
-      worker_->WakeUp();
-  }
-
   void EnableFlushPriorityQueueSequencesOnDestroyForTesting() {
     AutoSchedulerLock auto_lock(lock_);
     priority_queue_.EnableFlushSequencesOnDestroyForTesting();
   }
 
- protected:
-  scoped_refptr<Sequence> GetWorkLockRequired(SchedulerWorker* worker)
-      EXCLUSIVE_LOCKS_REQUIRED(lock_) {
-    if (!CanRunNextSequence()) {
-      return nullptr;
-    }
-    return priority_queue_.PopSequence();
-  }
-
-  const TrackedRef<TaskTracker>& task_tracker() { return task_tracker_; }
-
-  SchedulerLock lock_;
-  bool worker_awake_ GUARDED_BY(lock_) = false;
-
  private:
-  // Enqueues a sequence in this single-threaded worker's priority queue.
-  // Returns true iff the worker must wakeup, i.e. sequence is allowed to run
-  // and the worker was not awake.
-  bool EnqueueSequence(SequenceAndTransaction sequence_and_transaction) {
-    AutoSchedulerLock auto_lock(lock_);
-    priority_queue_.Push(std::move(sequence_and_transaction.sequence),
-                         sequence_and_transaction.transaction.GetSortKey());
-    if (!worker_awake_ && CanRunNextSequence()) {
-      worker_awake_ = true;
-      return true;
-    }
-    return false;
-  }
-
-  bool CanRunNextSequence() EXCLUSIVE_LOCKS_REQUIRED(lock_) {
-    return !priority_queue_.IsEmpty() &&
-           task_tracker_->CanRunPriority(
-               priority_queue_.PeekSortKey().priority());
-  }
-
   const std::string thread_name_;
   const SchedulerWorker::ThreadLabel thread_label_;
 
   // The SchedulerWorker that has |this| as a delegate. Must be set before
   // starting or posting a task to the SchedulerWorker, because it's used in
-  // OnMainEntry() and PostTaskNow().
+  // OnMainEntry() and OnCanScheduleSequence() (called when a sequence held up
+  // by WillScheduleSequence() in PostTaskNow() can be scheduled).
   SchedulerWorker* worker_ = nullptr;
 
-  const TrackedRef<TaskTracker> task_tracker_;
-
+  SchedulerLock lock_;
   PriorityQueue priority_queue_ GUARDED_BY(lock_);
 
   AtomicThreadRefChecker thread_ref_checker_;
@@ -215,9 +164,8 @@
   SchedulerWorkerCOMDelegate(const std::string& thread_name,
                              SchedulerWorker::ThreadLabel thread_label,
                              TrackedRef<TaskTracker> task_tracker)
-      : SchedulerWorkerDelegate(thread_name,
-                                thread_label,
-                                std::move(task_tracker)) {}
+      : SchedulerWorkerDelegate(thread_name, thread_label),
+        task_tracker_(std::move(task_tracker)) {}
 
   ~SchedulerWorkerCOMDelegate() override { DCHECK(!scoped_com_initializer_); }
 
@@ -237,16 +185,14 @@
     // * Both SchedulerWorkerDelegate::GetWork() and the Windows Message Queue
     //   have work:
     //   Process sequences from each source round-robin style.
-    AutoSchedulerLock auto_lock(lock_);
     scoped_refptr<Sequence> sequence;
     if (get_work_first_) {
-      sequence = SchedulerWorkerDelegate::GetWorkLockRequired(worker);
+      sequence = SchedulerWorkerDelegate::GetWork(worker);
       if (sequence)
         get_work_first_ = false;
     }
 
     if (!sequence) {
-      AutoSchedulerUnlock auto_unlock(lock_);
       sequence = GetWorkFromWindowsMessageQueue();
       if (sequence)
         get_work_first_ = true;
@@ -257,11 +203,7 @@
       // and found there was no work. We don't want to return null immediately
       // as that could cause the thread to go to sleep while work is waiting via
       // SchedulerWorkerDelegate::GetWork().
-      sequence = SchedulerWorkerDelegate::GetWorkLockRequired(worker);
-    }
-    if (sequence == nullptr) {
-      // The worker will sleep after this returns nullptr.
-      worker_awake_ = false;
+      sequence = SchedulerWorkerDelegate::GetWork(worker);
     }
     return sequence;
   }
@@ -276,12 +218,8 @@
     const DWORD milliseconds_wait = checked_cast<DWORD>(
         sleep_time.is_max() ? INFINITE : sleep_time.InMilliseconds());
     const HANDLE wake_up_event_handle = wake_up_event->handle();
-    DWORD reason = MsgWaitForMultipleObjectsEx(
-        1, &wake_up_event_handle, milliseconds_wait, QS_ALLINPUT, 0);
-    if (reason != WAIT_OBJECT_0) {
-      AutoSchedulerLock auto_lock(lock_);
-      worker_awake_ = true;
-    }
+    MsgWaitForMultipleObjectsEx(1, &wake_up_event_handle, milliseconds_wait,
+                                QS_ALLINPUT, 0);
   }
 
  private:
@@ -296,8 +234,8 @@
                                  },
                                  std::move(msg)),
                              TimeDelta());
-      if (task_tracker()->WillPostTask(
-              &pump_message_task, TaskShutdownBehavior::SKIP_ON_SHUTDOWN)) {
+      if (task_tracker_->WillPostTask(&pump_message_task,
+                                      TaskShutdownBehavior::SKIP_ON_SHUTDOWN)) {
         bool was_empty = message_pump_sequence_->BeginTransaction().PushTask(
             std::move(pump_message_task));
         DCHECK(was_empty) << "GetWorkFromWindowsMessageQueue() does not expect "
@@ -313,6 +251,7 @@
       MakeRefCounted<Sequence>(TaskTraits(MayBlock()),
                                nullptr,
                                TaskSourceExecutionMode::kParallel);
+  const TrackedRef<TaskTracker> task_tracker_;
   std::unique_ptr<win::ScopedCOMInitializer> scoped_com_initializer_;
 
   DISALLOW_COPY_AND_ASSIGN(SchedulerWorkerCOMDelegate);
@@ -358,15 +297,11 @@
     }
 
     if (task.delayed_run_time.is_null()) {
-      GetDelegate()->PostTaskNow(sequence_, std::move(task));
+      PostTaskNow(std::move(task));
     } else {
-      // Unretained(GetDelegate()) is safe because this TaskRunner and its
-      // worker are kept alive as long as there are pending Tasks.
       outer_->delayed_task_manager_->AddDelayedTask(
           std::move(task),
-          BindOnce(&SchedulerWorkerDelegate::PostTaskNow,
-                   Unretained(GetDelegate()), sequence_),
-          this);
+          BindOnce(&SchedulerSingleThreadTaskRunner::PostTaskNow, this), this);
     }
     return true;
   }
@@ -410,6 +345,20 @@
     }
   }
 
+  void PostTaskNow(Task task) {
+    auto sequence_and_transaction =
+        SequenceAndTransaction::FromSequence(sequence_);
+    const bool task_source_should_be_queued =
+        sequence_and_transaction.transaction.PushTask(std::move(task));
+    if (task_source_should_be_queued) {
+      if (outer_->task_tracker_->WillScheduleSequence(
+              sequence_and_transaction.transaction, GetDelegate())) {
+        GetDelegate()->ReEnqueueSequence(std::move(sequence_and_transaction));
+        worker_->WakeUp();
+      }
+    }
+  }
+
   SchedulerWorkerDelegate* GetDelegate() const {
     return static_cast<SchedulerWorkerDelegate*>(worker_->delegate());
   }
@@ -469,27 +418,8 @@
   // SchedulerSingleThreadTaskRunner::PostTaskNow(). As a result, it's
   // unnecessary to call WakeUp() for each worker (in fact, an extraneous
   // WakeUp() would be racy and wrong - see https://crbug.com/862582).
-  for (scoped_refptr<SchedulerWorker> worker : workers_to_start) {
+  for (scoped_refptr<SchedulerWorker> worker : workers_to_start)
     worker->Start(scheduler_worker_observer_);
-  }
-}
-
-void SchedulerSingleThreadTaskRunnerManager::DidUpdateCanRunPolicy() {
-  decltype(workers_) workers_to_update;
-
-  {
-    AutoSchedulerLock auto_lock(lock_);
-    if (!started_)
-      return;
-    workers_to_update = workers_;
-  }
-  // Any worker created after the lock is released will see the latest
-  // CanRunPolicy if tasks are posted to it and thus doesn't need a
-  // DidUpdateCanRunPolicy() notification.
-  for (auto& worker : workers_to_update) {
-    static_cast<SchedulerWorkerDelegate*>(worker->delegate())
-        ->DidUpdateCanRunPolicy();
-  }
 }
 
 scoped_refptr<SingleThreadTaskRunner>
@@ -605,8 +535,7 @@
       StringPrintf("TaskSchedulerSingleThread%s%d", name.c_str(), id),
       thread_mode == SingleThreadTaskRunnerThreadMode::DEDICATED
           ? SchedulerWorker::ThreadLabel::DEDICATED
-          : SchedulerWorker::ThreadLabel::SHARED,
-      task_tracker_);
+          : SchedulerWorker::ThreadLabel::SHARED);
 }
 
 #if defined(OS_WIN)
diff --git a/base/task/task_scheduler/scheduler_single_thread_task_runner_manager.h b/base/task/task_scheduler/scheduler_single_thread_task_runner_manager.h
index 9d33d3d..220dde2ce 100644
--- a/base/task/task_scheduler/scheduler_single_thread_task_runner_manager.h
+++ b/base/task/task_scheduler/scheduler_single_thread_task_runner_manager.h
@@ -63,10 +63,6 @@
   // JoinForTesting() has returned (must never be destroyed in production).
   void Start(SchedulerWorkerObserver* scheduler_worker_observer = nullptr);
 
-  // Wakes up workers as appropriate for the new CanRunPolicy policy. Must be
-  // called after an update to CanRunPolicy in TaskTracker.
-  void DidUpdateCanRunPolicy();
-
   // Creates a SingleThreadTaskRunner which runs tasks with |traits| on a thread
   // named "TaskSchedulerSingleThread[Shared]" +
   // kEnvironmentParams[GetEnvironmentIndexForTraits(traits)].name_suffix +
diff --git a/base/task/task_scheduler/scheduler_single_thread_task_runner_manager_unittest.cc b/base/task/task_scheduler/scheduler_single_thread_task_runner_manager_unittest.cc
index 65b50ef7..f33be2fb 100644
--- a/base/task/task_scheduler/scheduler_single_thread_task_runner_manager_unittest.cc
+++ b/base/task/task_scheduler/scheduler_single_thread_task_runner_manager_unittest.cc
@@ -11,14 +11,11 @@
 #include "base/synchronization/lock.h"
 #include "base/synchronization/waitable_event.h"
 #include "base/task/post_task.h"
-#include "base/task/task_scheduler/can_run_policy_test.h"
 #include "base/task/task_scheduler/delayed_task_manager.h"
 #include "base/task/task_scheduler/environment_config.h"
 #include "base/task/task_scheduler/scheduler_worker_pool_params.h"
 #include "base/task/task_scheduler/task_tracker.h"
-#include "base/task/task_scheduler/test_utils.h"
 #include "base/task/task_traits.h"
-#include "base/test/bind_test_util.h"
 #include "base/test/gtest_util.h"
 #include "base/test/test_timeouts.h"
 #include "base/threading/platform_thread.h"
@@ -41,7 +38,7 @@
 namespace {
 
 class TaskSchedulerSingleThreadTaskRunnerManagerTest : public testing::Test {
- protected:
+ public:
   TaskSchedulerSingleThreadTaskRunnerManagerTest()
       : service_thread_("TaskSchedulerServiceThread") {}
 
@@ -60,6 +57,7 @@
     service_thread_.Stop();
   }
 
+ protected:
   virtual void StartSingleThreadTaskRunnerManagerFromSetUp() {
     single_thread_task_runner_manager_->Start();
   }
@@ -118,7 +116,7 @@
   task_runner_2->PostTask(FROM_HERE,
                           BindOnce(&CaptureThreadRef, &thread_ref_2));
 
-  test::ShutdownTaskTracker(&task_tracker_);
+  task_tracker_.Shutdown();
 
   ASSERT_FALSE(thread_ref_1.is_null());
   ASSERT_FALSE(thread_ref_2.is_null());
@@ -144,7 +142,7 @@
   task_runner_2->PostTask(FROM_HERE,
                           BindOnce(&CaptureThreadRef, &thread_ref_2));
 
-  test::ShutdownTaskTracker(&task_tracker_);
+  task_tracker_.Shutdown();
 
   ASSERT_FALSE(thread_ref_1.is_null());
   ASSERT_FALSE(thread_ref_2.is_null());
@@ -187,7 +185,7 @@
           },
           task_runner_1, task_runner_2));
 
-  test::ShutdownTaskTracker(&task_tracker_);
+  task_tracker_.Shutdown();
 }
 
 TEST_F(TaskSchedulerSingleThreadTaskRunnerManagerTest,
@@ -232,7 +230,7 @@
       ->PostTask(FROM_HERE, DoNothing());
 
   // Shutdown should not hang even though the first task hasn't finished.
-  test::ShutdownTaskTracker(&task_tracker_);
+  task_tracker_.Shutdown();
 
   // Let the first task finish.
   task_can_continue.Signal();
@@ -250,12 +248,6 @@
  public:
   TaskSchedulerSingleThreadTaskRunnerManagerCommonTest() = default;
 
-  scoped_refptr<SingleThreadTaskRunner> CreateTaskRunner(
-      TaskTraits traits = TaskTraits()) {
-    return single_thread_task_runner_manager_
-        ->CreateSingleThreadTaskRunnerWithTraits(traits, GetParam());
-  }
-
  private:
   DISALLOW_COPY_AND_ASSIGN(
       TaskSchedulerSingleThreadTaskRunnerManagerCommonTest);
@@ -269,9 +261,13 @@
   // Shutting down can cause priorities to get raised. This means we have to use
   // events to determine when a task is run.
   scoped_refptr<SingleThreadTaskRunner> task_runner_background =
-      CreateTaskRunner({TaskPriority::BEST_EFFORT});
+      single_thread_task_runner_manager_
+          ->CreateSingleThreadTaskRunnerWithTraits({TaskPriority::BEST_EFFORT},
+                                                   GetParam());
   scoped_refptr<SingleThreadTaskRunner> task_runner_normal =
-      CreateTaskRunner({TaskPriority::USER_VISIBLE});
+      single_thread_task_runner_manager_
+          ->CreateSingleThreadTaskRunnerWithTraits({TaskPriority::USER_VISIBLE},
+                                                   GetParam());
 
   ThreadPriority thread_priority_background;
   task_runner_background->PostTask(
@@ -303,7 +299,8 @@
   constexpr TaskTraits foo_traits = {TaskPriority::BEST_EFFORT,
                                      TaskShutdownBehavior::BLOCK_SHUTDOWN};
   scoped_refptr<SingleThreadTaskRunner> foo_task_runner =
-      CreateTaskRunner(foo_traits);
+      single_thread_task_runner_manager_
+          ->CreateSingleThreadTaskRunnerWithTraits(foo_traits, GetParam());
   std::string foo_captured_name;
   foo_task_runner->PostTask(FROM_HERE,
                             BindOnce(&CaptureThreadName, &foo_captured_name));
@@ -320,7 +317,7 @@
   user_blocking_task_runner->PostTask(
       FROM_HERE, BindOnce(&CaptureThreadName, &user_blocking_captured_name));
 
-  test::ShutdownTaskTracker(&task_tracker_);
+  task_tracker_.Shutdown();
 
   EXPECT_NE(std::string::npos,
             foo_captured_name.find(
@@ -343,8 +340,10 @@
 
 TEST_P(TaskSchedulerSingleThreadTaskRunnerManagerCommonTest,
        PostTaskAfterShutdown) {
-  auto task_runner = CreateTaskRunner();
-  test::ShutdownTaskTracker(&task_tracker_);
+  auto task_runner =
+      single_thread_task_runner_manager_
+          ->CreateSingleThreadTaskRunnerWithTraits(TaskTraits(), GetParam());
+  task_tracker_.Shutdown();
   EXPECT_FALSE(task_runner->PostTask(FROM_HERE, BindOnce(&ShouldNotRun)));
 }
 
@@ -354,7 +353,9 @@
 
   WaitableEvent task_ran(WaitableEvent::ResetPolicy::AUTOMATIC,
                          WaitableEvent::InitialState::NOT_SIGNALED);
-  auto task_runner = CreateTaskRunner();
+  auto task_runner =
+      single_thread_task_runner_manager_
+          ->CreateSingleThreadTaskRunnerWithTraits(TaskTraits(), GetParam());
 
   // Wait until the task runner is up and running to make sure the test below is
   // solely timing the delayed task, not bringing up a physical thread.
@@ -383,30 +384,15 @@
 // but doesn't crash.
 TEST_P(TaskSchedulerSingleThreadTaskRunnerManagerCommonTest,
        PostTaskAfterDestroy) {
-  auto task_runner = CreateTaskRunner();
+  auto task_runner =
+      single_thread_task_runner_manager_
+          ->CreateSingleThreadTaskRunnerWithTraits(TaskTraits(), GetParam());
   EXPECT_TRUE(task_runner->PostTask(FROM_HERE, DoNothing()));
-  test::ShutdownTaskTracker(&task_tracker_);
+  task_tracker_.Shutdown();
   TearDownSingleThreadTaskRunnerManager();
   EXPECT_FALSE(task_runner->PostTask(FROM_HERE, BindOnce(&ShouldNotRun)));
 }
 
-// Verify that tasks only run when allowed by the CanRunPolicy.
-TEST_P(TaskSchedulerSingleThreadTaskRunnerManagerCommonTest,
-       CanRunPolicyBasic) {
-  test::TestCanRunPolicyBasic(
-      single_thread_task_runner_manager_.get(),
-      [this](TaskPriority priority) { return CreateTaskRunner({priority}); },
-      &task_tracker_);
-}
-
-TEST_P(TaskSchedulerSingleThreadTaskRunnerManagerCommonTest,
-       CanRunPolicyUpdatedBeforeRun) {
-  test::TestCanRunPolicyChangedBeforeRun(
-      single_thread_task_runner_manager_.get(),
-      [this](TaskPriority priority) { return CreateTaskRunner({priority}); },
-      &task_tracker_);
-}
-
 INSTANTIATE_TEST_SUITE_P(
     AllModes,
     TaskSchedulerSingleThreadTaskRunnerManagerCommonTest,
@@ -523,7 +509,7 @@
   com_task_runner->PostTask(FROM_HERE, BindOnce(&win::AssertComApartmentType,
                                                 win::ComApartmentType::STA));
 
-  test::ShutdownTaskTracker(&task_tracker_);
+  task_tracker_.Shutdown();
 }
 
 TEST_F(TaskSchedulerSingleThreadTaskRunnerManagerTest, COMSTASameThreadUsed) {
@@ -543,7 +529,7 @@
   task_runner_2->PostTask(FROM_HERE,
                           BindOnce(&CaptureThreadRef, &thread_ref_2));
 
-  test::ShutdownTaskTracker(&task_tracker_);
+  task_tracker_.Shutdown();
 
   ASSERT_FALSE(thread_ref_1.is_null());
   ASSERT_FALSE(thread_ref_2.is_null());
@@ -621,7 +607,7 @@
   com_task_runner->PostTask(
       FROM_HERE, BindOnce([](HWND hwnd) { ::DestroyWindow(hwnd); }, hwnd));
 
-  test::ShutdownTaskTracker(&task_tracker_);
+  task_tracker_.Shutdown();
 }
 
 #endif  // defined(OS_WIN)
diff --git a/base/task/task_scheduler/scheduler_worker.cc b/base/task/task_scheduler/scheduler_worker.cc
index deb098b8..c8d951c3 100644
--- a/base/task/task_scheduler/scheduler_worker.cc
+++ b/base/task/task_scheduler/scheduler_worker.cc
@@ -337,7 +337,8 @@
       continue;
     }
 
-    sequence = task_tracker_->RunAndPopNextTask(std::move(sequence));
+    sequence =
+        task_tracker_->RunAndPopNextTask(std::move(sequence), delegate_.get());
 
     delegate_->DidRunTask(std::move(sequence));
 
diff --git a/base/task/task_scheduler/scheduler_worker.h b/base/task/task_scheduler/scheduler_worker.h
index 37f4232d..88d1608 100644
--- a/base/task/task_scheduler/scheduler_worker.h
+++ b/base/task/task_scheduler/scheduler_worker.h
@@ -12,6 +12,7 @@
 #include "base/memory/ref_counted.h"
 #include "base/synchronization/atomic_flag.h"
 #include "base/synchronization/waitable_event.h"
+#include "base/task/task_scheduler/can_schedule_sequence_observer.h"
 #include "base/task/task_scheduler/scheduler_lock.h"
 #include "base/task/task_scheduler/scheduler_worker_params.h"
 #include "base/task/task_scheduler/sequence.h"
@@ -60,11 +61,12 @@
 #endif  // defined(OS_WIN)
   };
 
-  // Delegate interface for SchedulerWorker. All methods are called from the
-  // thread managed by the SchedulerWorker instance.
-  class BASE_EXPORT Delegate {
+  // Delegate interface for SchedulerWorker. All methods except
+  // OnCanScheduleSequence() (inherited from CanScheduleSequenceObserver) are
+  // called from the thread managed by the SchedulerWorker instance.
+  class BASE_EXPORT Delegate : public CanScheduleSequenceObserver {
    public:
-    virtual ~Delegate() = default;
+    ~Delegate() override = default;
 
     // Returns the ThreadLabel the Delegate wants its SchedulerWorkers' stacks
     // to be labeled with.
diff --git a/base/task/task_scheduler/scheduler_worker_pool.cc b/base/task/task_scheduler/scheduler_worker_pool.cc
index 0adb6d1..14e5e36 100644
--- a/base/task/task_scheduler/scheduler_worker_pool.cc
+++ b/base/task/task_scheduler/scheduler_worker_pool.cc
@@ -73,6 +73,17 @@
   return GetCurrentWorkerPool() == this;
 }
 
+void SchedulerWorkerPool::OnCanScheduleSequence(
+    scoped_refptr<Sequence> sequence) {
+  if (replacement_pool_) {
+    replacement_pool_->OnCanScheduleSequence(std::move(sequence));
+    return;
+  }
+
+  PushSequenceAndWakeUpWorkers(
+      SequenceAndTransaction::FromSequence(std::move(sequence)));
+}
+
 void SchedulerWorkerPool::PostTaskWithSequenceNow(
     Task task,
     SequenceAndTransaction sequence_and_transaction) {
@@ -85,31 +96,14 @@
   const bool task_source_should_be_queued =
       sequence_and_transaction.transaction.PushTask(std::move(task));
   if (task_source_should_be_queued) {
-    PushSequenceAndWakeUpWorkers(std::move(sequence_and_transaction));
+    // Try to schedule the Sequence locked by |sequence_transaction|.
+    if (task_tracker_->WillScheduleSequence(
+            sequence_and_transaction.transaction, this)) {
+      PushSequenceAndWakeUpWorkers(std::move(sequence_and_transaction));
+    }
   }
 }
 
-size_t SchedulerWorkerPool::GetNumQueuedCanRunBestEffortSequences() const {
-  const size_t num_queued =
-      priority_queue_.GetNumSequencesWithPriority(TaskPriority::BEST_EFFORT);
-  if (num_queued == 0 ||
-      !task_tracker_->CanRunPriority(TaskPriority::BEST_EFFORT)) {
-    return 0U;
-  }
-  return num_queued;
-}
-
-size_t SchedulerWorkerPool::GetNumQueuedCanRunForegroundSequences() const {
-  const size_t num_queued =
-      priority_queue_.GetNumSequencesWithPriority(TaskPriority::USER_VISIBLE) +
-      priority_queue_.GetNumSequencesWithPriority(TaskPriority::USER_BLOCKING);
-  if (num_queued == 0 ||
-      !task_tracker_->CanRunPriority(TaskPriority::HIGHEST)) {
-    return 0U;
-  }
-  return num_queued;
-}
-
 bool SchedulerWorkerPool::RemoveSequence(scoped_refptr<Sequence> sequence) {
   AutoSchedulerLock auto_lock(lock_);
   return priority_queue_.RemoveSequence(std::move(sequence));
diff --git a/base/task/task_scheduler/scheduler_worker_pool.h b/base/task/task_scheduler/scheduler_worker_pool.h
index 414e289..9c14fe3 100644
--- a/base/task/task_scheduler/scheduler_worker_pool.h
+++ b/base/task/task_scheduler/scheduler_worker_pool.h
@@ -7,6 +7,7 @@
 
 #include "base/base_export.h"
 #include "base/memory/ref_counted.h"
+#include "base/task/task_scheduler/can_schedule_sequence_observer.h"
 #include "base/task/task_scheduler/priority_queue.h"
 #include "base/task/task_scheduler/scheduler_lock.h"
 #include "base/task/task_scheduler/sequence.h"
@@ -19,8 +20,8 @@
 
 class TaskTracker;
 
-// Interface and base implementation for a worker pool.
-class BASE_EXPORT SchedulerWorkerPool {
+// Interface for a worker pool.
+class BASE_EXPORT SchedulerWorkerPool : public CanScheduleSequenceObserver {
  public:
   // Delegate interface for SchedulerWorkerPool.
   class BASE_EXPORT Delegate {
@@ -43,7 +44,10 @@
 #endif  // defined(OS_WIN)
   };
 
-  virtual ~SchedulerWorkerPool();
+  ~SchedulerWorkerPool() override;
+
+  // CanScheduleSequenceObserver:
+  void OnCanScheduleSequence(scoped_refptr<Sequence> sequence) final;
 
   // Posts |task| to be executed by this SchedulerWorkerPool as part of
   // the Sequence in |sequence_and_transaction|. This must only be called after
@@ -108,10 +112,6 @@
   // Reports relevant metrics per implementation.
   virtual void ReportHeartbeatMetrics() const = 0;
 
-  // Wakes up workers as appropriate for the new CanRunPolicy policy. Must be
-  // called after an update to CanRunPolicy in TaskTracker.
-  virtual void DidUpdateCanRunPolicy() = 0;
-
  protected:
   // Derived classes must implement a ScopedWorkersExecutor that derives from
   // this to perform operations on workers at the end of a scope, when all locks
@@ -156,16 +156,6 @@
   const TrackedRef<TaskTracker> task_tracker_;
   const TrackedRef<Delegate> delegate_;
 
-  // Returns the number of queued BEST_EFFORT sequences allowed to run by the
-  // current CanRunPolicy.
-  size_t GetNumQueuedCanRunBestEffortSequences() const
-      EXCLUSIVE_LOCKS_REQUIRED(lock_);
-
-  // Returns the number of queued USER_VISIBLE/USER_BLOCKING sequences allowed
-  // to run by the current CanRunPolicy.
-  size_t GetNumQueuedCanRunForegroundSequences() const
-      EXCLUSIVE_LOCKS_REQUIRED(lock_);
-
   // Ensures that there are enough workers to run queued sequences. |executor|
   // is forwarded from the one received in PushSequenceAndWakeUpWorkersImpl()
   virtual void EnsureEnoughWorkersLockRequired(
diff --git a/base/task/task_scheduler/scheduler_worker_pool_impl.cc b/base/task/task_scheduler/scheduler_worker_pool_impl.cc
index 286753c..1effafe 100644
--- a/base/task/task_scheduler/scheduler_worker_pool_impl.cc
+++ b/base/task/task_scheduler/scheduler_worker_pool_impl.cc
@@ -210,6 +210,7 @@
   ~SchedulerWorkerDelegateImpl() override;
 
   // SchedulerWorker::Delegate:
+  void OnCanScheduleSequence(scoped_refptr<Sequence> sequence) override;
   SchedulerWorker::ThreadLabel GetThreadLabel() const override;
   void OnMainEntry(const SchedulerWorker* worker) override;
   scoped_refptr<Sequence> GetWork(SchedulerWorker* worker) override;
@@ -563,6 +564,11 @@
 SchedulerWorkerPoolImpl::SchedulerWorkerDelegateImpl::
     ~SchedulerWorkerDelegateImpl() = default;
 
+void SchedulerWorkerPoolImpl::SchedulerWorkerDelegateImpl::
+    OnCanScheduleSequence(scoped_refptr<Sequence> sequence) {
+  outer_->OnCanScheduleSequence(std::move(sequence));
+}
+
 SchedulerWorker::ThreadLabel
 SchedulerWorkerPoolImpl::SchedulerWorkerDelegateImpl::GetThreadLabel() const {
   return SchedulerWorker::ThreadLabel::POOLED;
@@ -628,14 +634,13 @@
     return nullptr;
   }
 
-  // Enforce the CanRunPolicy and that no more than |max_best_effort_tasks_|
-  // BEST_EFFORT tasks run concurrently.
-  const TaskPriority priority =
-      outer_->priority_queue_.PeekSortKey().priority();
-  if (!outer_->task_tracker_->CanRunPriority(priority) ||
-      (priority == TaskPriority::BEST_EFFORT &&
-       outer_->num_running_best_effort_tasks_ >=
-           outer_->max_best_effort_tasks_)) {
+  // Enforce that no more than |max_best_effort_tasks_| BEST_EFFORT tasks run
+  // concurrently.
+  const bool next_sequence_is_best_effort =
+      outer_->priority_queue_.PeekSortKey().priority() ==
+      TaskPriority::BEST_EFFORT;
+  if (next_sequence_is_best_effort && outer_->num_running_best_effort_tasks_ >=
+                                          outer_->max_best_effort_tasks_) {
     OnWorkerBecomesIdleLockRequired(worker);
     return nullptr;
   }
@@ -646,7 +651,7 @@
   DCHECK(!outer_->idle_workers_stack_.Contains(worker));
 
   // Running BEST_EFFORT task bookkeeping.
-  if (priority == TaskPriority::BEST_EFFORT) {
+  if (next_sequence_is_best_effort) {
     write_worker().is_running_best_effort_task = true;
     ++outer_->num_running_best_effort_tasks_;
   }
@@ -1034,25 +1039,15 @@
 }
 
 size_t SchedulerWorkerPoolImpl::GetDesiredNumAwakeWorkersLockRequired() const {
-  if (!task_tracker_->CanRunPriority(TaskPriority::HIGHEST))
-    return 0U;
-
-  // Number of BEST_EFFORT sequences that are running or queued and allowed to
-  // run by the CanRunPolicy.
-  const size_t num_running_or_queued_can_run_best_effort_sequences =
-      num_running_best_effort_tasks_ + GetNumQueuedCanRunBestEffortSequences();
-
-  // Number of USER_{VISIBLE|BLOCKING} sequences that are running or queued.
+  const size_t num_running_or_queued_best_effort_sequences =
+      num_running_best_effort_tasks_ +
+      priority_queue_.GetNumSequencesWithPriority(TaskPriority::BEST_EFFORT);
   const size_t num_running_or_queued_foreground_sequences =
-      (num_running_tasks_ - num_running_best_effort_tasks_) +
-      priority_queue_.GetNumSequencesWithPriority(TaskPriority::USER_VISIBLE) +
-      priority_queue_.GetNumSequencesWithPriority(TaskPriority::USER_BLOCKING);
+      num_running_tasks_ + priority_queue_.Size() -
+      num_running_or_queued_best_effort_sequences;
 
-  const size_t workers_for_best_effort_sequences =
-      std::max(std::min(num_running_or_queued_can_run_best_effort_sequences,
-                        max_best_effort_tasks_),
-               num_running_best_effort_tasks_);
-
+  const size_t workers_for_best_effort_sequences = std::min(
+      num_running_or_queued_best_effort_sequences, max_best_effort_tasks_);
   const size_t workers_for_foreground_sequences =
       num_running_or_queued_foreground_sequences;
 
@@ -1061,12 +1056,6 @@
        max_tasks_, kMaxNumberOfWorkers});
 }
 
-void SchedulerWorkerPoolImpl::DidUpdateCanRunPolicy() {
-  ScopedWorkersExecutor executor(this);
-  AutoSchedulerLock auto_lock(lock_);
-  EnsureEnoughWorkersLockRequired(&executor);
-}
-
 void SchedulerWorkerPoolImpl::EnsureEnoughWorkersLockRequired(
     BaseScopedWorkersExecutor* base_executor) {
   // Don't do anything if the pool isn't started.
diff --git a/base/task/task_scheduler/scheduler_worker_pool_impl.h b/base/task/task_scheduler/scheduler_worker_pool_impl.h
index 3526d8e..6949e51 100644
--- a/base/task/task_scheduler/scheduler_worker_pool_impl.h
+++ b/base/task/task_scheduler/scheduler_worker_pool_impl.h
@@ -91,7 +91,6 @@
   void JoinForTesting() override;
   size_t GetMaxConcurrentNonBlockedTasksDeprecated() const override;
   void ReportHeartbeatMetrics() const override;
-  void DidUpdateCanRunPolicy() override;
 
   const HistogramBase* num_tasks_before_detach_histogram() const {
     return num_tasks_before_detach_histogram_;
diff --git a/base/task/task_scheduler/scheduler_worker_pool_impl_unittest.cc b/base/task/task_scheduler/scheduler_worker_pool_impl_unittest.cc
index 93afc77f..d3afa74 100644
--- a/base/task/task_scheduler/scheduler_worker_pool_impl_unittest.cc
+++ b/base/task/task_scheduler/scheduler_worker_pool_impl_unittest.cc
@@ -1044,9 +1044,9 @@
   DISALLOW_COPY_AND_ASSIGN(TaskSchedulerWorkerPoolBlockingTest);
 };
 
-// Verify that SaturateWithBlockingTasks() causes max tasks to increase and
-// creates a worker if needed. Also verify that UnblockBlockingTasks() decreases
-// max tasks after an increase.
+// Verify that BlockingScopeEntered() causes max tasks to increase and creates a
+// worker if needed. Also verify that BlockingScopeExited() decreases max tasks
+// after an increase.
 TEST_P(TaskSchedulerWorkerPoolBlockingTest, ThreadBlockedUnblocked) {
   CreateAndStartWorkerPool();
 
@@ -1066,69 +1066,6 @@
   EXPECT_EQ(worker_pool_->GetMaxTasksForTesting(), kMaxTasks);
 }
 
-// Verify that flooding the pool with more BEST_EFFORT tasks than
-// kMaxBestEffortTasks doesn't prevent USER_VISIBLE tasks from running.
-TEST_P(TaskSchedulerWorkerPoolBlockingTest, TooManyBestEffortTasks) {
-  constexpr size_t kMaxBestEffortTasks = kMaxTasks / 2;
-
-  CreateAndStartWorkerPool(TimeDelta::Max(), kMaxTasks, kMaxBestEffortTasks);
-
-  WaitableEvent threads_continue;
-  {
-    WaitableEvent entered_blocking_scope;
-    RepeatingClosure entered_blocking_scope_barrier = BarrierClosure(
-        kMaxBestEffortTasks + 1,
-        BindOnce(&WaitableEvent::Signal, Unretained(&entered_blocking_scope)));
-    WaitableEvent exit_blocking_scope;
-
-    WaitableEvent threads_running;
-    RepeatingClosure threads_running_barrier = BarrierClosure(
-        kMaxBestEffortTasks + 1,
-        BindOnce(&WaitableEvent::Signal, Unretained(&threads_running)));
-
-    const auto best_effort_task_runner = test::CreateTaskRunnerWithTraits(
-        {TaskPriority::BEST_EFFORT, MayBlock()},
-        &mock_scheduler_task_runner_delegate_);
-    for (size_t i = 0; i < kMaxBestEffortTasks + 1; ++i) {
-      best_effort_task_runner->PostTask(
-          FROM_HERE, BindLambdaForTesting([&]() {
-            {
-              NestedScopedBlockingCall scoped_blocking_call(GetParam());
-              entered_blocking_scope_barrier.Run();
-              test::WaitWithoutBlockingObserver(&exit_blocking_scope);
-            }
-            threads_running_barrier.Run();
-            test::WaitWithoutBlockingObserver(&threads_continue);
-          }));
-    }
-    entered_blocking_scope.Wait();
-    exit_blocking_scope.Signal();
-    threads_running.Wait();
-  }
-
-  // At this point, kMaxBestEffortTasks + 1 threads are running (plus
-  // potentially the idle thread), but max_task and max_best_effort_task are
-  // back to normal.
-  EXPECT_GE(worker_pool_->NumberOfWorkersForTesting(), kMaxBestEffortTasks + 1);
-  EXPECT_LE(worker_pool_->NumberOfWorkersForTesting(), kMaxBestEffortTasks + 2);
-  EXPECT_EQ(worker_pool_->GetMaxTasksForTesting(), kMaxTasks);
-
-  WaitableEvent threads_running;
-  task_runner_->PostTask(FROM_HERE, BindLambdaForTesting([&]() {
-                           threads_running.Signal();
-                           test::WaitWithoutBlockingObserver(&threads_continue);
-                         }));
-
-  // This should not block forever.
-  threads_running.Wait();
-
-  EXPECT_GE(worker_pool_->NumberOfWorkersForTesting(), kMaxBestEffortTasks + 2);
-  EXPECT_LE(worker_pool_->NumberOfWorkersForTesting(), kMaxBestEffortTasks + 3);
-  threads_continue.Signal();
-
-  task_tracker_.FlushForTesting();
-}
-
 // Verify that tasks posted in a saturated pool before a ScopedBlockingCall will
 // execute after ScopedBlockingCall is instantiated.
 TEST_P(TaskSchedulerWorkerPoolBlockingTest, PostBeforeBlocking) {
diff --git a/base/task/task_scheduler/scheduler_worker_pool_unittest.cc b/base/task/task_scheduler/scheduler_worker_pool_unittest.cc
index 25d3db5..c3b236c 100644
--- a/base/task/task_scheduler/scheduler_worker_pool_unittest.cc
+++ b/base/task/task_scheduler/scheduler_worker_pool_unittest.cc
@@ -11,7 +11,6 @@
 #include "base/bind_helpers.h"
 #include "base/location.h"
 #include "base/memory/ref_counted.h"
-#include "base/task/task_scheduler/can_run_policy_test.h"
 #include "base/task/task_scheduler/delayed_task_manager.h"
 #include "base/task/task_scheduler/scheduler_sequenced_task_runner.h"
 #include "base/task/task_scheduler/scheduler_worker_pool_impl.h"
@@ -167,13 +166,6 @@
     }
   }
 
-  scoped_refptr<TaskRunner> CreateTaskRunner(
-      const TaskTraits& traits = TaskTraits()) {
-    return test::CreateTaskRunnerWithExecutionMode(
-        GetParam().execution_mode, &mock_scheduler_task_runner_delegate_,
-        traits);
-  }
-
   Thread service_thread_;
   TaskTracker task_tracker_ = {"Test"};
   DelayedTaskManager delayed_task_manager_;
@@ -248,8 +240,9 @@
 // Verify that a Task can't be posted after shutdown.
 TEST_P(TaskSchedulerWorkerPoolTest, PostTaskAfterShutdown) {
   StartWorkerPool();
-  auto task_runner = CreateTaskRunner();
-  test::ShutdownTaskTracker(&task_tracker_);
+  auto task_runner = test::CreateTaskRunnerWithExecutionMode(
+      GetParam().execution_mode, &mock_scheduler_task_runner_delegate_);
+  task_tracker_.Shutdown();
   EXPECT_FALSE(task_runner->PostTask(FROM_HERE, BindOnce(&ShouldNotRun)));
 }
 
@@ -257,9 +250,10 @@
 // crash.
 TEST_P(TaskSchedulerWorkerPoolTest, PostAfterDestroy) {
   StartWorkerPool();
-  auto task_runner = CreateTaskRunner();
+  auto task_runner = test::CreateTaskRunnerWithExecutionMode(
+      GetParam().execution_mode, &mock_scheduler_task_runner_delegate_);
   EXPECT_TRUE(task_runner->PostTask(FROM_HERE, DoNothing()));
-  test::ShutdownTaskTracker(&task_tracker_);
+  task_tracker_.Shutdown();
   worker_pool_->JoinForTesting();
   worker_pool_.reset();
   EXPECT_FALSE(task_runner->PostTask(FROM_HERE, BindOnce(&ShouldNotRun)));
@@ -271,7 +265,9 @@
 
   WaitableEvent task_ran(WaitableEvent::ResetPolicy::AUTOMATIC,
                          WaitableEvent::InitialState::NOT_SIGNALED);
-  auto task_runner = CreateTaskRunner();
+
+  auto task_runner = test::CreateTaskRunnerWithExecutionMode(
+      GetParam().execution_mode, &mock_scheduler_task_runner_delegate_);
 
   // Wait until the task runner is up and running to make sure the test below is
   // solely timing the delayed task, not bringing up a physical thread.
@@ -304,7 +300,8 @@
 // complements it to get full coverage of that method.
 TEST_P(TaskSchedulerWorkerPoolTest, SequencedRunsTasksInCurrentSequence) {
   StartWorkerPool();
-  auto task_runner = CreateTaskRunner();
+  auto task_runner = test::CreateTaskRunnerWithExecutionMode(
+      GetParam().execution_mode, &mock_scheduler_task_runner_delegate_);
   auto sequenced_task_runner = test::CreateSequencedTaskRunnerWithTraits(
       TaskTraits(), &mock_scheduler_task_runner_delegate_);
 
@@ -326,7 +323,9 @@
   WaitableEvent task_1_running;
   WaitableEvent task_2_running;
 
-  auto task_runner = CreateTaskRunner();
+  scoped_refptr<TaskRunner> task_runner = test::CreateTaskRunnerWithTraits(
+      {WithBaseSyncPrimitives()}, &mock_scheduler_task_runner_delegate_);
+
   task_runner->PostTask(
       FROM_HERE, BindOnce(&WaitableEvent::Signal, Unretained(&task_1_running)));
   task_runner->PostTask(
@@ -347,27 +346,6 @@
   task_tracker_.FlushForTesting();
 }
 
-// Verify that tasks only run when allowed by the CanRunPolicy.
-TEST_P(TaskSchedulerWorkerPoolTest, CanRunPolicyBasic) {
-  StartWorkerPool();
-  test::TestCanRunPolicyBasic(
-      worker_pool_.get(),
-      [this](TaskPriority priority) { return CreateTaskRunner({priority}); },
-      &task_tracker_);
-}
-
-TEST_P(TaskSchedulerWorkerPoolTest, CanRunPolicyUpdatedBeforeRun) {
-  StartWorkerPool();
-  // This test only works with SequencedTaskRunner become it assumes
-  // ordered execution of 2 posted tasks.
-  if (GetParam().execution_mode != test::ExecutionMode::SEQUENCED)
-    return;
-  test::TestCanRunPolicyChangedBeforeRun(
-      worker_pool_.get(),
-      [this](TaskPriority priority) { return CreateTaskRunner({priority}); },
-      &task_tracker_);
-}
-
 // Verify that the maximum number of BEST_EFFORT tasks that can run concurrently
 // in a pool does not affect Sequences with a priority that was increased from
 // BEST_EFFORT to USER_BLOCKING.
diff --git a/base/task/task_scheduler/scheduler_worker_stack_unittest.cc b/base/task/task_scheduler/scheduler_worker_stack_unittest.cc
index f94d2a7..f78243b 100644
--- a/base/task/task_scheduler/scheduler_worker_stack_unittest.cc
+++ b/base/task/task_scheduler/scheduler_worker_stack_unittest.cc
@@ -21,6 +21,9 @@
 
 class MockSchedulerWorkerDelegate : public SchedulerWorker::Delegate {
  public:
+  void OnCanScheduleSequence(scoped_refptr<Sequence> sequence) override {
+    ADD_FAILURE() << "Unexpected call to OnCanScheduleSequence().";
+  }
   SchedulerWorker::ThreadLabel GetThreadLabel() const override {
     return SchedulerWorker::ThreadLabel::DEDICATED;
   }
diff --git a/base/task/task_scheduler/scheduler_worker_unittest.cc b/base/task/task_scheduler/scheduler_worker_unittest.cc
index 9803f81..4e9bac7 100644
--- a/base/task/task_scheduler/scheduler_worker_unittest.cc
+++ b/base/task/task_scheduler/scheduler_worker_unittest.cc
@@ -53,6 +53,9 @@
   SchedulerWorkerDefaultDelegate() = default;
 
   // SchedulerWorker::Delegate:
+  void OnCanScheduleSequence(scoped_refptr<Sequence> sequence) override {
+    ADD_FAILURE() << "Unexpected call to OnCanScheduleSequence().";
+  }
   SchedulerWorker::ThreadLabel GetThreadLabel() const override {
     return SchedulerWorker::ThreadLabel::DEDICATED;
   }
@@ -195,6 +198,8 @@
         outer_->created_sequences_.push_back(sequence);
       }
 
+      EXPECT_TRUE(outer_->task_tracker_.WillScheduleSequence(
+          sequence_transaction, nullptr));
       return sequence;
     }
 
@@ -457,7 +462,10 @@
         TimeDelta());
     EXPECT_TRUE(
         task_tracker_->WillPostTask(&task, sequence->shutdown_behavior()));
-    sequence->BeginTransaction().PushTask(std::move(task));
+    Sequence::Transaction sequence_transaction(sequence->BeginTransaction());
+    sequence_transaction.PushTask(std::move(task));
+    EXPECT_TRUE(
+        task_tracker_->WillScheduleSequence(sequence_transaction, nullptr));
     return sequence;
   }
 
@@ -593,7 +601,7 @@
   worker->WakeUp();
 
   controls->WaitForWorkToRun();
-  test::ShutdownTaskTracker(&task_tracker);
+  task_tracker.Shutdown();
   worker->Cleanup();
   worker = nullptr;
   controls->UnblockWork();
@@ -737,11 +745,6 @@
 
   TaskTracker task_tracker("Test");
 
-  // Block shutdown to ensure that the worker doesn't exit when StartShutdown()
-  // is called.
-  Task task(FROM_HERE, DoNothing(), TimeDelta());
-  task_tracker.WillPostTask(&task, TaskShutdownBehavior::BLOCK_SHUTDOWN);
-
   std::unique_ptr<ExpectThreadPriorityDelegate> delegate(
       new ExpectThreadPriorityDelegate);
   ExpectThreadPriorityDelegate* delegate_raw = delegate.get();
@@ -758,7 +761,7 @@
 
   // Verify that the thread priority is bumped to NORMAL during shutdown.
   delegate_raw->SetExpectedThreadPriority(ThreadPriority::NORMAL);
-  task_tracker.StartShutdown();
+  task_tracker.SetHasShutdownStartedForTesting();
   worker->WakeUp();
   delegate_raw->WaitForPriorityVerifiedInGetWork();
 
diff --git a/base/task/task_scheduler/task_scheduler.cc b/base/task/task_scheduler/task_scheduler.cc
index 6075029a7..02c9297 100644
--- a/base/task/task_scheduler/task_scheduler.cc
+++ b/base/task/task_scheduler/task_scheduler.cc
@@ -35,12 +35,12 @@
 
 TaskScheduler::ScopedExecutionFence::ScopedExecutionFence() {
   DCHECK(g_task_scheduler);
-  g_task_scheduler->SetCanRun(false);
+  g_task_scheduler->SetExecutionFenceEnabled(true);
 }
 
 TaskScheduler::ScopedExecutionFence::~ScopedExecutionFence() {
   DCHECK(g_task_scheduler);
-  g_task_scheduler->SetCanRun(true);
+  g_task_scheduler->SetExecutionFenceEnabled(false);
 }
 
 #if !defined(OS_NACL)
diff --git a/base/task/task_scheduler/task_scheduler.h b/base/task/task_scheduler/task_scheduler.h
index 02bad08..185e8536 100644
--- a/base/task/task_scheduler/task_scheduler.h
+++ b/base/task/task_scheduler/task_scheduler.h
@@ -209,9 +209,8 @@
   virtual int GetMaxConcurrentNonBlockedTasksWithTraitsDeprecated(
       const TaskTraits& traits) const = 0;
 
-  // Sets whether tasks of any / BEST_EFFORT priority are allowed to run.
-  virtual void SetCanRun(bool can_run) = 0;
-  virtual void SetCanRunBestEffort(bool can_run) = 0;
+  // Enables/disables an execution fence that prevents tasks from running.
+  virtual void SetExecutionFenceEnabled(bool execution_fence_enabled) = 0;
 };
 
 }  // namespace base
diff --git a/base/task/task_scheduler/task_scheduler_impl.cc b/base/task/task_scheduler/task_scheduler_impl.cc
index 8b71a26..7acd6f68 100644
--- a/base/task/task_scheduler/task_scheduler_impl.cc
+++ b/base/task/task_scheduler/task_scheduler_impl.cc
@@ -8,10 +8,8 @@
 #include <string>
 #include <utility>
 
-#include "base/base_switches.h"
 #include "base/bind.h"
 #include "base/bind_helpers.h"
-#include "base/command_line.h"
 #include "base/compiler_specific.h"
 #include "base/feature_list.h"
 #include "base/message_loop/message_loop.h"
@@ -40,15 +38,6 @@
 constexpr EnvironmentParams kBackgroundPoolEnvironmentParams{
     "Background", base::ThreadPriority::BACKGROUND};
 
-// Indicates whether BEST_EFFORT tasks are disabled by a command line switch.
-bool HasDisableBestEffortTasksSwitch() {
-  // The CommandLine might not be initialized if TaskScheduler is initialized
-  // in a dynamic library which doesn't have access to argc/argv.
-  return CommandLine::InitializedForCurrentProcess() &&
-         CommandLine::ForCurrentProcess()->HasSwitch(
-             switches::kDisableBestEffortTasks);
-}
-
 }  // namespace
 
 TaskSchedulerImpl::TaskSchedulerImpl(StringPiece histogram_label)
@@ -65,7 +54,6 @@
                         Unretained(this)))),
       single_thread_task_runner_manager_(task_tracker_->GetTrackedRef(),
                                          &delayed_task_manager_),
-      can_run_best_effort_(!HasDisableBestEffortTasksSwitch()),
       tracked_ref_factory_(this) {
   DCHECK(!histogram_label.empty());
 
@@ -103,9 +91,6 @@
 void TaskSchedulerImpl::Start(
     const TaskScheduler::InitParams& init_params,
     SchedulerWorkerObserver* scheduler_worker_observer) {
-  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-  DCHECK(!started_);
-
   internal::InitializeThreadPrioritiesFeature();
 
   // This is set in Start() and not in the constructor because variation params
@@ -187,8 +172,6 @@
         service_thread_task_runner, scheduler_worker_observer,
         worker_environment);
   }
-
-  started_ = true;
 }
 
 bool TaskSchedulerImpl::PostDelayedTaskWithTraits(const Location& from_here,
@@ -252,18 +235,7 @@
 }
 
 void TaskSchedulerImpl::Shutdown() {
-  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-
-  task_tracker_->StartShutdown();
-
-  // Allow all tasks to run. Done after initiating shutdown to ensure that non-
-  // BLOCK_SHUTDOWN tasks don't get a chance to run and that BLOCK_SHUTDOWN
-  // tasks run with a normal thread priority.
-  can_run_ = true;
-  can_run_best_effort_ = true;
-  UpdateCanRunPolicy();
-
-  task_tracker_->CompleteShutdown();
+  task_tracker_->Shutdown();
 }
 
 void TaskSchedulerImpl::FlushForTesting() {
@@ -292,18 +264,8 @@
 #endif
 }
 
-void TaskSchedulerImpl::SetCanRun(bool can_run) {
-  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-  DCHECK_NE(can_run_, can_run);
-  can_run_ = can_run;
-  UpdateCanRunPolicy();
-}
-
-void TaskSchedulerImpl::SetCanRunBestEffort(bool can_run_best_effort) {
-  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-  DCHECK_NE(can_run_best_effort_, can_run_best_effort);
-  can_run_best_effort_ = can_run_best_effort;
-  UpdateCanRunPolicy();
+void TaskSchedulerImpl::SetExecutionFenceEnabled(bool execution_fence_enabled) {
+  task_tracker_->SetExecutionFenceEnabled(execution_fence_enabled);
 }
 
 bool TaskSchedulerImpl::PostTaskWithSequence(Task task,
@@ -406,20 +368,6 @@
   return &foreground_pool_.value();
 }
 
-void TaskSchedulerImpl::UpdateCanRunPolicy() {
-  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-
-  const CanRunPolicy can_run_policy =
-      can_run_ ? (can_run_best_effort_ ? CanRunPolicy::kAll
-                                       : CanRunPolicy::kForegroundOnly)
-               : CanRunPolicy::kNone;
-  task_tracker_->SetCanRunPolicy(can_run_policy);
-  GetForegroundWorkerPool()->DidUpdateCanRunPolicy();
-  if (background_pool_)
-    background_pool_->DidUpdateCanRunPolicy();
-  single_thread_task_runner_manager_.DidUpdateCanRunPolicy();
-}
-
 TaskTraits TaskSchedulerImpl::SetUserBlockingPriorityIfNeeded(
     TaskTraits traits) const {
   if (all_tasks_user_blocking_.IsSet())
diff --git a/base/task/task_scheduler/task_scheduler_impl.h b/base/task/task_scheduler/task_scheduler_impl.h
index ab70eb5..adad3a4 100644
--- a/base/task/task_scheduler/task_scheduler_impl.h
+++ b/base/task/task_scheduler/task_scheduler_impl.h
@@ -14,7 +14,6 @@
 #include "base/macros.h"
 #include "base/memory/ptr_util.h"
 #include "base/memory/ref_counted.h"
-#include "base/sequence_checker.h"
 #include "base/strings/string_piece.h"
 #include "base/synchronization/atomic_flag.h"
 #include "base/task/single_thread_task_runner_thread_mode.h"
@@ -79,8 +78,7 @@
   void FlushForTesting() override;
   void FlushAsyncForTesting(OnceClosure flush_callback) override;
   void JoinForTesting() override;
-  void SetCanRun(bool can_run) override;
-  void SetCanRunBestEffort(bool can_run_best_effort) override;
+  void SetExecutionFenceEnabled(bool execution_fence_enabled) override;
 
   // TaskExecutor:
   bool PostDelayedTaskWithTraits(const Location& from_here,
@@ -104,10 +102,6 @@
       const TaskTraits& traits);
 
  private:
-  // Invoked after |can_run_| or |can_run_best_effort_| is updated. Sets the
-  // CanRunPolicy in TaskTracker and wakes up workers as appropriate.
-  void UpdateCanRunPolicy();
-
   // Returns |traits|, with priority set to TaskPriority::USER_BLOCKING if
   // |all_tasks_user_blocking_| is set.
   TaskTraits SetUserBlockingPriorityIfNeeded(TaskTraits traits) const;
@@ -148,15 +142,6 @@
   Optional<SchedulerWorkerPoolImpl> foreground_pool_;
   Optional<SchedulerWorkerPoolImpl> background_pool_;
 
-  // Whether this TaskScheduler was started. Access controlled by
-  // |sequence_checker_|.
-  bool started_ = false;
-
-  // Whether starting to run a Task with any/BEST_EFFORT priority is currently
-  // allowed. Access controlled by |sequence_checker_|.
-  bool can_run_ = true;
-  bool can_run_best_effort_;
-
 #if defined(OS_WIN)
   Optional<PlatformNativeWorkerPoolWin> native_foreground_pool_;
 #elif defined(OS_MACOSX)
@@ -173,9 +158,6 @@
   base::win::ComInitCheckHook com_init_check_hook_;
 #endif
 
-  // Asserts that operations occur in sequence with Start().
-  SEQUENCE_CHECKER(sequence_checker_);
-
   TrackedRefFactory<SchedulerWorkerPool::Delegate> tracked_ref_factory_;
 
   DISALLOW_COPY_AND_ASSIGN(TaskSchedulerImpl);
diff --git a/base/task/task_scheduler/task_scheduler_impl_unittest.cc b/base/task/task_scheduler/task_scheduler_impl_unittest.cc
index 7187c196..f5c710ef 100644
--- a/base/task/task_scheduler/task_scheduler_impl_unittest.cc
+++ b/base/task/task_scheduler/task_scheduler_impl_unittest.cc
@@ -495,75 +495,6 @@
   flush_event.Wait();
 }
 
-// Verifies that tasks only run when allowed by SetCanRun().
-TEST_P(TaskSchedulerImplTest, SetCanRun) {
-  StartTaskScheduler();
-
-  AtomicFlag can_run;
-  WaitableEvent did_run;
-  scheduler_.SetCanRun(false);
-
-  CreateTaskRunnerWithTraitsAndExecutionMode(&scheduler_, GetParam().traits,
-                                             GetParam().execution_mode)
-      ->PostTask(FROM_HERE, BindLambdaForTesting([&]() {
-                   EXPECT_TRUE(can_run.IsSet());
-                   did_run.Signal();
-                 }));
-
-  PlatformThread::Sleep(TestTimeouts::tiny_timeout());
-
-  can_run.Set();
-  scheduler_.SetCanRun(true);
-  did_run.Wait();
-}
-
-// Verifies that a call to SetCanRun(false) before Start() is honored.
-TEST_P(TaskSchedulerImplTest, SetCanRunBeforeStart) {
-  scheduler_.SetCanRun(false);
-  StartTaskScheduler();
-
-  AtomicFlag can_run;
-  WaitableEvent did_run;
-
-  CreateTaskRunnerWithTraitsAndExecutionMode(&scheduler_, GetParam().traits,
-                                             GetParam().execution_mode)
-      ->PostTask(FROM_HERE, BindLambdaForTesting([&]() {
-                   EXPECT_TRUE(can_run.IsSet());
-                   did_run.Signal();
-                 }));
-
-  PlatformThread::Sleep(TestTimeouts::tiny_timeout());
-
-  can_run.Set();
-  scheduler_.SetCanRun(true);
-  did_run.Wait();
-}
-
-// Verifies that BEST_EFFORT tasks only run when allowed by
-// SetCanRunBestEffort().
-TEST_P(TaskSchedulerImplTest, SetCanRunBestEffort) {
-  StartTaskScheduler();
-
-  AtomicFlag can_run;
-  WaitableEvent did_run;
-  scheduler_.SetCanRunBestEffort(false);
-
-  CreateTaskRunnerWithTraitsAndExecutionMode(&scheduler_, GetParam().traits,
-                                             GetParam().execution_mode)
-      ->PostTask(
-          FROM_HERE, BindLambdaForTesting([&]() {
-            if (GetParam().traits.priority() == TaskPriority::BEST_EFFORT)
-              EXPECT_TRUE(can_run.IsSet());
-            did_run.Signal();
-          }));
-
-  PlatformThread::Sleep(TestTimeouts::tiny_timeout());
-
-  can_run.Set();
-  scheduler_.SetCanRunBestEffort(true);
-  did_run.Wait();
-}
-
 INSTANTIATE_TEST_SUITE_P(OneTaskSchedulerImplTestParams,
                          TaskSchedulerImplTest,
                          ::testing::ValuesIn(GetTaskSchedulerImplTestParams()));
diff --git a/base/task/task_scheduler/task_tracker.cc b/base/task/task_scheduler/task_tracker.cc
index 211f1261..6a218ab3 100644
--- a/base/task/task_scheduler/task_tracker.cc
+++ b/base/task/task_scheduler/task_tracker.cc
@@ -8,7 +8,9 @@
 #include <string>
 #include <vector>
 
+#include "base/base_switches.h"
 #include "base/callback.h"
+#include "base/command_line.h"
 #include "base/compiler_specific.h"
 #include "base/debug/alias.h"
 #include "base/json/json_writer.h"
@@ -128,6 +130,19 @@
                         : 0];
 }
 
+// Returns the maximum number of TaskPriority::BEST_EFFORT sequences that can be
+// scheduled concurrently based on command line flags.
+int GetMaxNumScheduledBestEffortSequences() {
+  // The CommandLine might not be initialized if TaskScheduler is initialized
+  // in a dynamic library which doesn't have access to argc/argv.
+  if (CommandLine::InitializedForCurrentProcess() &&
+      CommandLine::ForCurrentProcess()->HasSwitch(
+          switches::kDisableBestEffortTasks)) {
+    return 0;
+  }
+  return std::numeric_limits<int>::max();
+}
+
 // Returns shutdown behavior based on |traits|; returns SKIP_ON_SHUTDOWN if
 // shutdown behavior is BLOCK_SHUTDOWN and |is_delayed|, because delayed tasks
 // are not allowed to block shutdown.
@@ -229,10 +244,47 @@
   DISALLOW_COPY_AND_ASSIGN(State);
 };
 
-// TODO(jessemckenna): Write a helper function to avoid code duplication below.
+struct TaskTracker::PreemptedSequence {
+  PreemptedSequence() = default;
+  PreemptedSequence(scoped_refptr<Sequence> sequence_in,
+                    TimeTicks next_task_sequenced_time_in,
+                    CanScheduleSequenceObserver* observer_in)
+      : sequence(std::move(sequence_in)),
+        next_task_sequenced_time(next_task_sequenced_time_in),
+        observer(observer_in) {}
+  PreemptedSequence(PreemptedSequence&& other) = default;
+  ~PreemptedSequence() = default;
+  PreemptedSequence& operator=(PreemptedSequence&& other) = default;
+  bool operator<(const PreemptedSequence& other) const {
+    return next_task_sequenced_time < other.next_task_sequenced_time;
+  }
+  bool operator>(const PreemptedSequence& other) const {
+    return next_task_sequenced_time > other.next_task_sequenced_time;
+  }
+
+  // A sequence waiting to be scheduled.
+  scoped_refptr<Sequence> sequence;
+
+  // The sequenced time of the next task in |sequence|.
+  TimeTicks next_task_sequenced_time;
+
+  // An observer to notify when |sequence| can be scheduled.
+  CanScheduleSequenceObserver* observer = nullptr;
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(PreemptedSequence);
+};
+
+TaskTracker::PreemptionState::PreemptionState() = default;
+TaskTracker::PreemptionState::~PreemptionState() = default;
+
 TaskTracker::TaskTracker(StringPiece histogram_label)
+    : TaskTracker(histogram_label, GetMaxNumScheduledBestEffortSequences()) {}
+
+// TODO(jessemckenna): Write a helper function to avoid code duplication below.
+TaskTracker::TaskTracker(StringPiece histogram_label,
+                         int max_num_scheduled_best_effort_sequences)
     : state_(new State),
-      can_run_policy_(CanRunPolicy::kAll),
       flush_cv_(flush_lock_.CreateConditionVariable()),
       shutdown_lock_(&flush_lock_),
       task_latency_histograms_{
@@ -297,44 +349,50 @@
   DCHECK(*(&task_latency_histograms_[static_cast<int>(TaskPriority::HIGHEST) +
                                      1][0] -
            1));
+  preemption_state_[static_cast<int>(TaskPriority::BEST_EFFORT)]
+      .max_scheduled_sequences = max_num_scheduled_best_effort_sequences;
+  DETACH_FROM_SEQUENCE(sequence_checker_);
 }
 
 TaskTracker::~TaskTracker() = default;
 
-void TaskTracker::StartShutdown() {
-  AutoSchedulerLock auto_lock(shutdown_lock_);
+void TaskTracker::SetExecutionFenceEnabled(bool execution_fence_enabled) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
 
-  // This method can only be called once.
-  DCHECK(!shutdown_event_);
-  DCHECK(!state_->HasShutdownStarted());
+#if DCHECK_IS_ON()
+  // It is invalid to have two fences at the same time.
+  DCHECK_NE(execution_fence_enabled_, execution_fence_enabled);
+  execution_fence_enabled_ = execution_fence_enabled;
+#endif
 
-  shutdown_event_ = std::make_unique<WaitableEvent>();
+  for (int priority_index = static_cast<int>(TaskPriority::HIGHEST);
+       priority_index >= static_cast<int>(TaskPriority::LOWEST);
+       --priority_index) {
+    int max_scheduled_sequences;
+    if (execution_fence_enabled) {
+      preemption_state_[priority_index].max_scheduled_sequences_before_fence =
+          preemption_state_[priority_index].max_scheduled_sequences;
+      max_scheduled_sequences = 0;
+    } else {
+      max_scheduled_sequences = preemption_state_[priority_index]
+                                    .max_scheduled_sequences_before_fence;
+    }
 
-  const bool tasks_are_blocking_shutdown = state_->StartShutdown();
-
-  // From now, if a thread causes the number of tasks blocking shutdown to
-  // become zero, it will call OnBlockingShutdownTasksComplete().
-
-  if (!tasks_are_blocking_shutdown) {
-    // If another thread posts a BLOCK_SHUTDOWN task at this moment, it will
-    // block until this method releases |shutdown_lock_|. Then, it will fail
-    // DCHECK(!shutdown_event_->IsSignaled()). This is the desired behavior
-    // because posting a BLOCK_SHUTDOWN task after StartShutdown() when no
-    // tasks are blocking shutdown isn't allowed.
-    shutdown_event_->Signal();
-    return;
+    SetMaxNumScheduledSequences(max_scheduled_sequences,
+                                static_cast<TaskPriority>(priority_index));
   }
 }
 
-void TaskTracker::CompleteShutdown() {
-  DCHECK(shutdown_event_);
-  // It is safe to access |shutdown_event_| without holding |lock_| because the
-  // pointer never changes after being set by StartShutdown(), which must be
-  // called before this.
-  {
-    base::ScopedAllowBaseSyncPrimitives allow_wait;
-    shutdown_event_->Wait();
-  }
+size_t TaskTracker::GetPreemptedSequenceCountForTesting(
+    TaskPriority task_priority) {
+  int priority_index = static_cast<int>(task_priority);
+  AutoSchedulerLock auto_lock(preemption_state_[priority_index].lock);
+  return preemption_state_[priority_index].preempted_sequences.size();
+}
+
+void TaskTracker::Shutdown() {
+  PerformShutdown();
+  DCHECK(IsShutdownComplete());
 
   // Unblock FlushForTesting() and perform the FlushAsyncForTesting callback
   // when shutdown completes.
@@ -368,10 +426,6 @@
   }
 }
 
-void TaskTracker::SetCanRunPolicy(CanRunPolicy can_run_policy) {
-  can_run_policy_.store(can_run_policy);
-}
-
 bool TaskTracker::WillPostTask(Task* task,
                                TaskShutdownBehavior shutdown_behavior) {
   DCHECK(task);
@@ -390,22 +444,33 @@
   return true;
 }
 
-bool TaskTracker::CanRunPriority(TaskPriority priority) const {
-  auto can_run_policy = can_run_policy_.load();
+bool TaskTracker::WillScheduleSequence(
+    const Sequence::Transaction& sequence_transaction,
+    CanScheduleSequenceObserver* observer) {
+  const SequenceSortKey sort_key = sequence_transaction.GetSortKey();
+  const int priority_index = static_cast<int>(sort_key.priority());
 
-  if (can_run_policy == CanRunPolicy::kAll)
-    return true;
+  AutoSchedulerLock auto_lock(preemption_state_[priority_index].lock);
 
-  if (can_run_policy == CanRunPolicy::kForegroundOnly &&
-      priority >= TaskPriority::USER_VISIBLE) {
+  if (preemption_state_[priority_index].current_scheduled_sequences <
+      preemption_state_[priority_index].max_scheduled_sequences) {
+    ++preemption_state_[priority_index].current_scheduled_sequences;
     return true;
   }
 
+  // It is convenient not to have to specify an observer when scheduling
+  // foreground sequences in tests.
+  DCHECK(observer);
+
+  preemption_state_[priority_index].preempted_sequences.emplace(
+      WrapRefCounted(sequence_transaction.sequence()),
+      sort_key.next_task_sequenced_time(), observer);
   return false;
 }
 
 scoped_refptr<Sequence> TaskTracker::RunAndPopNextTask(
-    scoped_refptr<Sequence> sequence) {
+    scoped_refptr<Sequence> sequence,
+    CanScheduleSequenceObserver* observer) {
   DCHECK(sequence);
 
   // Run the next task in |sequence|.
@@ -438,10 +503,15 @@
   const bool sequence_must_be_queued =
       sequence->BeginTransaction().DidRunTask();
 
-  // The sequence should be reenqueued iff requested by DidRunTask().
+  // Never reschedule a Sequence empty after DidRunTask(). The contract is such
+  // that next poster to make it non-empty is responsible to schedule it.
   if (!sequence_must_be_queued)
-    return nullptr;
-  return sequence;
+    sequence = nullptr;
+
+  // Allow |sequence| to be rescheduled only if its next task is set to run
+  // earlier than the earliest currently preempted sequence
+  return ManageSequencesAfterRunningTask(std::move(sequence), observer,
+                                         traits.priority());
 }
 
 bool TaskTracker::HasShutdownStarted() const {
@@ -453,6 +523,16 @@
   return shutdown_event_ && shutdown_event_->IsSignaled();
 }
 
+void TaskTracker::SetHasShutdownStartedForTesting() {
+  AutoSchedulerLock auto_lock(shutdown_lock_);
+
+  // Create a dummy |shutdown_event_| to satisfy TaskTracker's expectation of
+  // its existence during shutdown (e.g. in OnBlockingShutdownTasksComplete()).
+  shutdown_event_ = std::make_unique<WaitableEvent>();
+
+  state_->StartShutdown();
+}
+
 void TaskTracker::RecordLatencyHistogram(
     LatencyHistogramType latency_histogram_type,
     TaskTraits task_traits,
@@ -575,6 +655,105 @@
   ThreadRestrictions::SetSingletonAllowed(previous_singleton_allowed);
 }
 
+void TaskTracker::PerformShutdown() {
+  {
+    AutoSchedulerLock auto_lock(shutdown_lock_);
+
+    // This method can only be called once.
+    DCHECK(!shutdown_event_);
+    DCHECK(!state_->HasShutdownStarted());
+
+    shutdown_event_ = std::make_unique<WaitableEvent>();
+
+    const bool tasks_are_blocking_shutdown = state_->StartShutdown();
+
+    // From now, if a thread causes the number of tasks blocking shutdown to
+    // become zero, it will call OnBlockingShutdownTasksComplete().
+
+    if (!tasks_are_blocking_shutdown) {
+      // If another thread posts a BLOCK_SHUTDOWN task at this moment, it will
+      // block until this method releases |shutdown_lock_|. Then, it will fail
+      // DCHECK(!shutdown_event_->IsSignaled()). This is the desired behavior
+      // because posting a BLOCK_SHUTDOWN task when TaskTracker::Shutdown() has
+      // started and no tasks are blocking shutdown isn't allowed.
+      shutdown_event_->Signal();
+      return;
+    }
+  }
+
+  // Remove the cap on the maximum number of sequences that can be scheduled
+  // concurrently. Done after starting shutdown to ensure that non-
+  // BLOCK_SHUTDOWN sequences don't get a chance to run and that BLOCK_SHUTDOWN
+  // sequences run on threads running with a normal priority.
+  for (int priority_index = static_cast<int>(TaskPriority::HIGHEST);
+       priority_index >= static_cast<int>(TaskPriority::LOWEST);
+       --priority_index) {
+    SetMaxNumScheduledSequences(std::numeric_limits<int>::max(),
+                                static_cast<TaskPriority>(priority_index));
+  }
+
+  // It is safe to access |shutdown_event_| without holding |lock_| because the
+  // pointer never changes after being set above.
+  {
+    base::ScopedAllowBaseSyncPrimitives allow_wait;
+    shutdown_event_->Wait();
+  }
+}
+
+void TaskTracker::SetMaxNumScheduledSequences(int max_scheduled_sequences,
+                                              TaskPriority task_priority) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+
+  std::vector<PreemptedSequence> sequences_to_schedule;
+  int priority_index = static_cast<int>(task_priority);
+
+  {
+    AutoSchedulerLock auto_lock(preemption_state_[priority_index].lock);
+    preemption_state_[priority_index].max_scheduled_sequences =
+        max_scheduled_sequences;
+
+    while (preemption_state_[priority_index].current_scheduled_sequences <
+               max_scheduled_sequences &&
+           !preemption_state_[priority_index].preempted_sequences.empty()) {
+      sequences_to_schedule.push_back(
+          GetPreemptedSequenceToScheduleLockRequired(task_priority));
+    }
+  }
+
+  for (auto& sequence_to_schedule : sequences_to_schedule)
+    SchedulePreemptedSequence(std::move(sequence_to_schedule));
+}
+
+TaskTracker::PreemptedSequence
+TaskTracker::GetPreemptedSequenceToScheduleLockRequired(
+    TaskPriority task_priority) {
+  int priority_index = static_cast<int>(task_priority);
+
+  preemption_state_[priority_index].lock.AssertAcquired();
+  DCHECK(!preemption_state_[priority_index].preempted_sequences.empty());
+
+  ++preemption_state_[priority_index].current_scheduled_sequences;
+  DCHECK_LE(preemption_state_[priority_index].current_scheduled_sequences,
+            preemption_state_[priority_index].max_scheduled_sequences);
+
+  // The const_cast on top is okay since the PreemptedSequence is
+  // transactionnaly being popped from
+  // |preemption_state_[priority_index].preempted_sequences| right after and the
+  // move doesn't alter the sort order (a requirement for the Windows STL's
+  // consistency debug-checks for std::priority_queue::top()).
+  PreemptedSequence popped_sequence = std::move(const_cast<PreemptedSequence&>(
+      preemption_state_[priority_index].preempted_sequences.top()));
+  preemption_state_[priority_index].preempted_sequences.pop();
+  return popped_sequence;
+}
+
+void TaskTracker::SchedulePreemptedSequence(
+    PreemptedSequence sequence_to_schedule) {
+  DCHECK(sequence_to_schedule.observer);
+  sequence_to_schedule.observer->OnCanScheduleSequence(
+      std::move(sequence_to_schedule.sequence));
+}
+
 bool TaskTracker::HasIncompleteUndelayedTasksForTesting() const {
   return subtle::Acquire_Load(&num_incomplete_undelayed_tasks_) != 0;
 }
@@ -690,6 +869,56 @@
   }
 }
 
+scoped_refptr<Sequence> TaskTracker::ManageSequencesAfterRunningTask(
+    scoped_refptr<Sequence> just_ran_sequence,
+    CanScheduleSequenceObserver* observer,
+    TaskPriority task_priority) {
+  const TimeTicks next_task_sequenced_time =
+      just_ran_sequence ? just_ran_sequence->BeginTransaction()
+                              .GetSortKey()
+                              .next_task_sequenced_time()
+                        : TimeTicks();
+  PreemptedSequence sequence_to_schedule;
+  int priority_index = static_cast<int>(task_priority);
+
+  {
+    AutoSchedulerLock auto_lock(preemption_state_[priority_index].lock);
+
+    --preemption_state_[priority_index].current_scheduled_sequences;
+
+    const bool can_schedule_sequence =
+        preemption_state_[priority_index].current_scheduled_sequences <
+        preemption_state_[priority_index].max_scheduled_sequences;
+
+    if (just_ran_sequence) {
+      if (can_schedule_sequence &&
+          (preemption_state_[priority_index].preempted_sequences.empty() ||
+           preemption_state_[priority_index]
+                   .preempted_sequences.top()
+                   .next_task_sequenced_time > next_task_sequenced_time)) {
+        ++preemption_state_[priority_index].current_scheduled_sequences;
+        return just_ran_sequence;
+      }
+
+      preemption_state_[priority_index].preempted_sequences.emplace(
+          std::move(just_ran_sequence), next_task_sequenced_time, observer);
+    }
+
+    if (can_schedule_sequence &&
+        !preemption_state_[priority_index].preempted_sequences.empty()) {
+      sequence_to_schedule =
+          GetPreemptedSequenceToScheduleLockRequired(task_priority);
+    }
+  }
+
+  // |sequence_to_schedule.sequence| may be null if there was no preempted
+  // sequence.
+  if (sequence_to_schedule.sequence)
+    SchedulePreemptedSequence(std::move(sequence_to_schedule));
+
+  return nullptr;
+}
+
 void TaskTracker::CallFlushCallbackForTesting() {
   OnceClosure flush_callback;
   {
diff --git a/base/task/task_scheduler/task_tracker.h b/base/task/task_scheduler/task_tracker.h
index 2ce7f95..8dd9fe4 100644
--- a/base/task/task_scheduler/task_tracker.h
+++ b/base/task/task_scheduler/task_tracker.h
@@ -21,6 +21,7 @@
 #include "base/strings/string_piece.h"
 #include "base/synchronization/waitable_event.h"
 #include "base/task/common/task_annotator.h"
+#include "base/task/task_scheduler/can_schedule_sequence_observer.h"
 #include "base/task/task_scheduler/scheduler_lock.h"
 #include "base/task/task_scheduler/sequence.h"
 #include "base/task/task_scheduler/task.h"
@@ -34,42 +35,85 @@
 
 namespace internal {
 
-// Determines which tasks are allowed to run.
-enum class CanRunPolicy {
-  // All tasks are allowed to run.
-  kAll,
-  // Only USER_VISIBLE and USER_BLOCKING tasks are allowed to run.
-  kForegroundOnly,
-  // No tasks can run.
-  kNone,
-};
-
 // TaskTracker enforces policies that determines whether:
 // - A task can be added to a sequence (WillPostTask).
-// - Tasks for a given priority can run (CanRunPriority).
+// - A sequence can be scheduled (WillScheduleSequence).
 // - The next task in a scheduled sequence can run (RunAndPopNextTask).
 // TaskTracker also sets up the environment to run a task (RunAndPopNextTask)
 // and records metrics and trace events. This class is thread-safe.
+//
+// Life of a sequence:
+// (possible states: IDLE, PREEMPTED, SCHEDULED, RUNNING)
+//
+//                            Create a sequence
+//                                   |
+//  ------------------------> Sequence is IDLE
+//  |                                |
+//  |                     Add a task to the sequence
+//  |            (allowed by TaskTracker::WillPostTask)
+//  |                                |
+//  |               TaskTracker:WillScheduleSequence
+//  |           _____________________|_____________________
+//  |           |                                          |
+//  |    Returns true                                Returns false
+//  |           |                                          |
+//  |           |                                Sequence is PREEMPTED <----
+//  |           |                                          |               |
+//  |           |                            Eventually,                   |
+//  |           |                            CanScheduleSequenceObserver   |
+//  |           |                            is notified that the          |
+//  |           |                            sequence can be scheduled.    |
+//  |           |__________________________________________|               |
+//  |                               |                                      |
+//  |                   (*) Sequence is SCHEDULED                          |
+//  |                               |                                      |
+//  |                A thread is ready to run the next                     |
+//  |                      task in the sequence                            |
+//  |                               |                                      |
+//  |                TaskTracker::RunAndPopNextTask                        |
+//  |                A task from the sequence is run                       |
+//  |                      Sequence is RUNNING                             |
+//  |                               |                                      |
+//  |         ______________________|____                                  |
+//  |         |                          |                                 |
+//  |   Sequence is empty      Sequence has more tasks                     |
+//  |_________|             _____________|_______________                  |
+//                          |                            |                 |
+//                   Sequence can be            Sequence cannot be         |
+//                   scheduled                  scheduled at this          |
+//                          |                   moment                     |
+//                   Go back to (*)                      |_________________|
+//
+//
+// Note: A best-effort task is a task posted with TaskPriority::BEST_EFFORT. A
+// foreground task is a task posted with TaskPriority::USER_VISIBLE or
+// TaskPriority::USER_BLOCKING.
+//
+// TODO(fdoray): We want to allow disabling TaskPriority::BEST_EFFORT tasks in a
+// scope (e.g. during startup or page load), but we don't need a dynamic maximum
+// number of best-effort tasks. The code could probably be simplified if it
+// didn't support that. https://crbug.com/831835
 class BASE_EXPORT TaskTracker {
  public:
   // |histogram_label| is used as a suffix for histograms, it must not be empty.
+  // The first constructor sets the maximum number of TaskPriority::BEST_EFFORT
+  // sequences that can be scheduled concurrently to 0 if the
+  // --disable-best-effort-tasks flag is specified, max() otherwise. The second
+  // constructor sets it to |max_num_scheduled_best_effort_sequences|.
   TaskTracker(StringPiece histogram_label);
+  TaskTracker(StringPiece histogram_label,
+              int max_num_scheduled_best_effort_sequences);
 
   virtual ~TaskTracker();
 
-  // Initiates shutdown. Once this is called, only BLOCK_SHUTDOWN tasks will
-  // start running (doesn't affect tasks that are already running). This can
-  // only be called once.
-  void StartShutdown();
-
-  // Synchronously completes shutdown. StartShutdown() must be called first.
-  // Returns when:
+  // Synchronously shuts down the scheduler. Once this is called, only tasks
+  // posted with the BLOCK_SHUTDOWN behavior will be run. Returns when:
   // - All SKIP_ON_SHUTDOWN tasks that were already running have completed their
   //   execution.
   // - All posted BLOCK_SHUTDOWN tasks have completed their execution.
   // CONTINUE_ON_SHUTDOWN tasks still may be running after Shutdown returns.
   // This can only be called once.
-  void CompleteShutdown();
+  void Shutdown();
 
   // Waits until there are no incomplete undelayed tasks. May be called in tests
   // to validate that a condition is met after all undelayed tasks have run.
@@ -85,34 +129,45 @@
   // FlushAsyncForTesting() may be pending at any given time.
   void FlushAsyncForTesting(OnceClosure flush_callback);
 
-  // Sets the new CanRunPolicy policy, possibly affecting result of
-  // CanRunPriority(). The caller must wake up worker as appropriate so that
-  // tasks that are allowed to run by the new policy can be scheduled.
-  void SetCanRunPolicy(CanRunPolicy can_run_policy);
-
   // Informs this TaskTracker that |task| from a |shutdown_behavior| sequence
   // is about to be posted. Returns true if this operation is allowed (|task|
   // should be posted if-and-only-if it is). This method may also modify
   // metadata on |task| if desired.
   bool WillPostTask(Task* task, TaskShutdownBehavior shutdown_behavior);
 
-  // Returns true if a task with |priority| can run under to the current policy.
-  bool CanRunPriority(TaskPriority priority) const;
+  // Informs this TaskTracker that the Sequence locked by |sequence_transaction|
+  // is about to be scheduled. If this returns true, it is expected that
+  // RunAndPopNextTask() will soon be called with the Sequence as argument.
+  // Otherwise, RunAndPopNextTask() must not be called with the Sequence as
+  // argument until |observer| is notified that the Sequence can be scheduled
+  // (the caller doesn't need to keep a pointer to the Sequence; it will be
+  // included in the notification to |observer|). WillPostTask() must have
+  // allowed the task in front of the Sequence to be posted before this is
+  // called. |observer| is only required if the priority of the Sequence is
+  // TaskPriority::BEST_EFFORT.
+  bool WillScheduleSequence(const Sequence::Transaction& sequence_transaction,
+                            CanScheduleSequenceObserver* observer);
 
   // Runs the next task in |sequence| unless the current shutdown state prevents
   // that. Then, pops the task from |sequence| (even if it didn't run). Returns
-  // |sequence| if non-empty after popping a task from it (which indicates that
-  // it should be reenqueued). WillPostTask() must have allowed the task in
-  // front of |sequence| to be posted before this is called.
-  scoped_refptr<Sequence> RunAndPopNextTask(scoped_refptr<Sequence> sequence);
+  // |sequence| if it can be rescheduled immediately. If |sequence| is non-empty
+  // after popping a task from it but it can't be rescheduled immediately, it
+  // will be handed back to |observer| when it can be rescheduled.
+  // WillPostTask() must have allowed the task in front of |sequence| to be
+  // posted before this is called. Also, WillScheduleSequence(),
+  // RunAndPopNextTask() or CanScheduleSequenceObserver::OnCanScheduleSequence()
+  // must have allowed |sequence| to be (re)scheduled.
+  scoped_refptr<Sequence> RunAndPopNextTask(
+      scoped_refptr<Sequence> sequence,
+      CanScheduleSequenceObserver* observer);
 
-  // Returns true once shutdown has started (StartShutdown() was called).
-  // Note: sequential consistency with the thread calling StartShutdown() isn't
-  // guaranteed by this call.
+  // Returns true once shutdown has started (Shutdown() has been called but
+  // might not have returned). Note: sequential consistency with the thread
+  // calling Shutdown() (or SetHasShutdownStartedForTesting()) isn't guaranteed
+  // by this call.
   bool HasShutdownStarted() const;
 
-  // Returns true if shutdown has completed (StartShutdown() was called and
-  // no tasks are blocking shutdown).
+  // Returns true if shutdown has completed (Shutdown() has returned).
   bool IsShutdownComplete() const;
 
   enum class LatencyHistogramType {
@@ -125,6 +180,11 @@
     HEARTBEAT_LATENCY,
   };
 
+  // Causes HasShutdownStarted() to return true. Unlike when Shutdown() returns,
+  // IsShutdownComplete() won't return true after this returns. Shutdown()
+  // cannot be called after this.
+  void SetHasShutdownStartedForTesting();
+
   // Records two histograms
   // 1. TaskScheduler.[label].HeartbeatLatencyMicroseconds.[suffix]:
   //    Now() - posted_time
@@ -145,6 +205,13 @@
     return tracked_ref_factory_.GetTrackedRef();
   }
 
+  // Enables/disables an execution fence. When the fence is released,
+  // reschedules the sequences that were preempted by the fence.
+  void SetExecutionFenceEnabled(bool execution_fence_enabled);
+
+  // Returns the number of preempted sequences of a given priority.
+  size_t GetPreemptedSequenceCountForTesting(TaskPriority priority);
+
  protected:
   // Runs and deletes |task| if |can_run_task| is true. Otherwise, just deletes
   // |task|. |task| is always deleted in the environment where it runs or would
@@ -164,9 +231,62 @@
 
  private:
   class State;
+  struct PreemptedSequence;
+
+  struct PreemptionState {
+    PreemptionState();
+    ~PreemptionState();
+
+    // A priority queue of sequences that are waiting to be scheduled. Use
+    // std::greater so that the sequence which contains the task that has been
+    // posted the earliest is on top of the priority queue.
+    std::priority_queue<PreemptedSequence,
+                        std::vector<PreemptedSequence>,
+                        std::greater<PreemptedSequence>>
+        preempted_sequences;
+
+    // Maximum number of sequences that can that be scheduled concurrently.
+    int max_scheduled_sequences = std::numeric_limits<int>::max();
+
+    // Caches the |max_scheduled_sequences| before enabling the execution fence.
+    int max_scheduled_sequences_before_fence = 0;
+
+    // Number of currently scheduled sequences.
+    int current_scheduled_sequences = 0;
+
+    // Synchronizes accesses to other members.
+    // |max_scheduled_sequences| and |max_scheduled_sequences_before_fence| are
+    // only written from the main sequence within the scope of |lock|. Reads can
+    // happen on the main sequence without holding |lock|, or on any other
+    // sequence while holding |lock|.
+    SchedulerLock lock;
+
+   private:
+    DISALLOW_COPY_AND_ASSIGN(PreemptionState);
+  };
 
   void PerformShutdown();
 
+  // Sets the maximum number of sequences of priority |priority| that can be
+  // scheduled concurrently to |max_scheduled_sequences|.
+  void SetMaxNumScheduledSequences(int max_scheduled_sequences,
+                                   TaskPriority priority);
+
+  // Pops the next sequence in |preemption_state_[priority].preempted_sequences|
+  // and increments |preemption_state_[priority].current_scheduled_sequences|.
+  // Must only be called in the scope of |preemption_state_[priority].lock|,
+  // with |preemption_state_[priority].preempted_sequences| non-empty. The
+  // caller must forward the returned sequence to the associated
+  // CanScheduleSequenceObserver as soon as |preemption_state_[priority].lock|
+  // is released.
+  PreemptedSequence GetPreemptedSequenceToScheduleLockRequired(
+      TaskPriority priority);
+
+  // Schedules |sequence_to_schedule.sequence| using
+  // |sequence_to_schedule.observer|. Does not verify that the sequence is
+  // allowed to be scheduled.
+  void SchedulePreemptedSequence(PreemptedSequence sequence_to_schedule);
+
   // Called before WillPostTask() informs the tracing system that a task has
   // been posted. Updates |num_tasks_blocking_shutdown_| if necessary and
   // returns true if the current shutdown state allows the task to be posted.
@@ -190,6 +310,24 @@
   // if it reaches zero.
   void DecrementNumIncompleteUndelayedTasks();
 
+  // To be called after running a task from |just_ran_sequence|. Performs the
+  // following actions:
+  //  - If |just_ran_sequence| is non-null:
+  //    - returns it if it should be rescheduled by the caller of
+  //      RunAndPopNextTask(), i.e. its next task is set to run earlier than the
+  //      earliest currently preempted sequence.
+  //    - Otherwise |just_ran_sequence| is preempted and the next preempted
+  //      sequence is scheduled (|observer| will be notified when
+  //      |just_ran_sequence| should be scheduled again).
+  //  - If |just_ran_sequence| is null (RunAndPopNextTask() just popped the last
+  //    task from it):
+  //    - the next preempeted sequence (if any) is scheduled.
+  //  - In all cases: adjusts the number of scheduled sequences accordingly.
+  scoped_refptr<Sequence> ManageSequencesAfterRunningTask(
+      scoped_refptr<Sequence> just_ran_sequence,
+      CanScheduleSequenceObserver* observer,
+      TaskPriority task_priority);
+
   // Calls |flush_callback_for_testing_| if one is available in a lock-safe
   // manner.
   void CallFlushCallbackForTesting();
@@ -222,9 +360,6 @@
   // returns.
   subtle::Atomic32 num_incomplete_undelayed_tasks_ = 0;
 
-  // Global policy the determines result of CanRunPriority().
-  std::atomic<CanRunPolicy> can_run_policy_;
-
   // Lock associated with |flush_cv_|. Partially synchronizes access to
   // |num_incomplete_undelayed_tasks_|. Full synchronization isn't needed
   // because it's atomic, but synchronization is needed to coordinate waking and
@@ -265,6 +400,19 @@
   HistogramBase* const
       num_tasks_run_while_queuing_histograms_[kNumTaskPriorities][2];
 
+  PreemptionState preemption_state_[kNumTaskPriorities];
+
+#if DCHECK_IS_ON()
+  // Indicates whether to prevent tasks running.
+  bool execution_fence_enabled_ = false;
+#endif
+
+  // Enforces that |max_scheduled_sequences| and
+  // |max_scheduled_sequences_before_fence| in PreemptedState are only written
+  // on the main sequence (determined by the first call to
+  // SetMaxNumScheduledSequences or SetExecutionFenceEnabled).
+  SEQUENCE_CHECKER(sequence_checker_);
+
   // Ensures all state (e.g. dangling cleaned up workers) is coalesced before
   // destroying the TaskTracker (e.g. in test environments).
   // Ref. https://crbug.com/827615.
diff --git a/base/task/task_scheduler/task_tracker_posix_unittest.cc b/base/task/task_scheduler/task_tracker_posix_unittest.cc
index f0a49dd..8d35b08 100644
--- a/base/task/task_scheduler/task_tracker_posix_unittest.cc
+++ b/base/task/task_scheduler/task_tracker_posix_unittest.cc
@@ -61,9 +61,11 @@
   EXPECT_TRUE(tracker_.WillPostTask(&task, default_traits.shutdown_behavior()));
 
   auto sequence = test::CreateSequenceWithTask(std::move(task), default_traits);
+  EXPECT_TRUE(
+      tracker_.WillScheduleSequence(sequence->BeginTransaction(), nullptr));
   // Expect RunAndPopNextTask to return nullptr since |sequence| is empty after
   // popping a task from it.
-  EXPECT_FALSE(tracker_.RunAndPopNextTask(sequence));
+  EXPECT_FALSE(tracker_.RunAndPopNextTask(sequence, nullptr));
 
   EXPECT_TRUE(did_run);
 }
@@ -85,10 +87,12 @@
   auto sequence = test::CreateSequenceWithTask(
       std::move(task), default_traits, MakeRefCounted<NullTaskRunner>(),
       TaskSourceExecutionMode::kSequenced);
+  EXPECT_TRUE(
+      tracker_.WillScheduleSequence(sequence->BeginTransaction(), nullptr));
 
   // Expect RunAndPopNextTask to return nullptr since |sequence| is empty after
   // popping a task from it.
-  EXPECT_FALSE(tracker_.RunAndPopNextTask(sequence));
+  EXPECT_FALSE(tracker_.RunAndPopNextTask(sequence, nullptr));
 
   // Join the service thread to make sure that the read watch is registered and
   // unregistered before file descriptors are closed.
diff --git a/base/task/task_scheduler/task_tracker_unittest.cc b/base/task/task_scheduler/task_tracker_unittest.cc
index a5ef426..798dd823 100644
--- a/base/task/task_scheduler/task_tracker_unittest.cc
+++ b/base/task/task_scheduler/task_tracker_unittest.cc
@@ -48,6 +48,15 @@
 
 constexpr size_t kLoadTestNumIterations = 75;
 
+class MockCanScheduleSequenceObserver : public CanScheduleSequenceObserver {
+ public:
+  void OnCanScheduleSequence(scoped_refptr<Sequence> sequence) override {
+    MockOnCanScheduleSequence(sequence.get());
+  }
+
+  MOCK_METHOD1(MockOnCanScheduleSequence, void(Sequence*));
+};
+
 // Invokes a closure asynchronously.
 class CallbackThread : public SimpleThread {
  public:
@@ -123,10 +132,16 @@
         (action_ == Action::RUN || action_ == Action::WILL_POST_AND_RUN)) {
       EXPECT_TRUE(owned_task_.task);
 
+      testing::StrictMock<MockCanScheduleSequenceObserver>
+          never_notified_observer;
+      auto sequence =
+          test::CreateSequenceWithTask(std::move(owned_task_), traits_);
+      ASSERT_TRUE(tracker_->WillScheduleSequence(sequence->BeginTransaction(),
+                                                 &never_notified_observer));
       // Expect RunAndPopNextTask to return nullptr since |sequence| is empty
       // after popping a task from it.
-      EXPECT_FALSE(tracker_->RunAndPopNextTask(
-          test::CreateSequenceWithTask(std::move(owned_task_), traits_)));
+      EXPECT_FALSE(tracker_->RunAndPopNextTask(std::move(sequence),
+                                               &never_notified_observer));
     }
   }
 
@@ -167,19 +182,22 @@
   }
 
   void DispatchAndRunTaskWithTracker(Task task, const TaskTraits& traits) {
-    tracker_.RunAndPopNextTask(
-        test::CreateSequenceWithTask(std::move(task), traits));
+    auto sequence = test::CreateSequenceWithTask(std::move(task), traits);
+    ASSERT_TRUE(tracker_.WillScheduleSequence(sequence->BeginTransaction(),
+                                              &never_notified_observer_));
+    tracker_.RunAndPopNextTask(std::move(sequence), &never_notified_observer_);
   }
 
-  // Calls tracker_->CompleteShutdown() on a new thread and expects it to block.
-  void ExpectAsyncCompleteShutdownBlocks() {
+  // Calls tracker_->Shutdown() on a new thread. When this returns, Shutdown()
+  // method has been entered on the new thread, but it hasn't necessarily
+  // returned.
+  void CallShutdownAsync() {
     ASSERT_FALSE(thread_calling_shutdown_);
-    ASSERT_TRUE(tracker_.HasShutdownStarted());
-    thread_calling_shutdown_ = std::make_unique<CallbackThread>(
-        Bind(&TaskTracker::CompleteShutdown, Unretained(&tracker_)));
+    thread_calling_shutdown_.reset(new CallbackThread(
+        Bind(&TaskTracker::Shutdown, Unretained(&tracker_))));
     thread_calling_shutdown_->Start();
-    PlatformThread::Sleep(TestTimeouts::tiny_timeout());
-    VerifyAsyncShutdownInProgress();
+    while (!tracker_.HasShutdownStarted())
+      PlatformThread::YieldCurrentThread();
   }
 
   void WaitForAsyncIsShutdownComplete() {
@@ -221,6 +239,7 @@
   }
 
   TaskTracker tracker_ = {"Test"};
+  testing::StrictMock<MockCanScheduleSequenceObserver> never_notified_observer_;
 
  private:
   void RunTaskCallback() {
@@ -278,7 +297,7 @@
   EXPECT_EQ(1U, NumTasksExecuted());
 
   // Shutdown() shouldn't block.
-  test::ShutdownTaskTracker(&tracker_);
+  tracker_.Shutdown();
 }
 
 TEST_P(TaskSchedulerTaskTrackerTest, WillPostAndRunLongTaskBeforeShutdown) {
@@ -310,14 +329,14 @@
   task_running.Wait();
 
   // Initiate shutdown after the task has started to run.
-  tracker_.StartShutdown();
+  CallShutdownAsync();
 
   if (GetParam() == TaskShutdownBehavior::CONTINUE_ON_SHUTDOWN) {
     // Shutdown should complete even with a CONTINUE_ON_SHUTDOWN in progress.
-    tracker_.CompleteShutdown();
+    WAIT_FOR_ASYNC_SHUTDOWN_COMPLETED();
   } else {
     // Shutdown should block with any non CONTINUE_ON_SHUTDOWN task in progress.
-    ExpectAsyncCompleteShutdownBlocks();
+    VERIFY_ASYNC_SHUTDOWN_IN_PROGRESS();
   }
 
   // Unblock the task.
@@ -340,9 +359,9 @@
   EXPECT_TRUE(tracker_.WillPostTask(&block_shutdown_task,
                                     TaskShutdownBehavior::BLOCK_SHUTDOWN));
 
-  // Start shutdown and try to complete it asynchronously.
-  tracker_.StartShutdown();
-  ExpectAsyncCompleteShutdownBlocks();
+  // Call Shutdown() asynchronously.
+  CallShutdownAsync();
+  VERIFY_ASYNC_SHUTDOWN_IN_PROGRESS();
 
   // Try to run |task|. It should only run it it's BLOCK_SHUTDOWN. Otherwise it
   // should be discarded.
@@ -365,13 +384,12 @@
   Task task(CreateTask());
   EXPECT_TRUE(tracker_.WillPostTask(&task, GetParam()));
 
-  // Start shutdown.
-  tracker_.StartShutdown();
+  // Call Shutdown() asynchronously.
+  CallShutdownAsync();
   EXPECT_EQ(0U, NumTasksExecuted());
 
   if (GetParam() == TaskShutdownBehavior::BLOCK_SHUTDOWN) {
-    // Verify that CompleteShutdown() blocks.
-    ExpectAsyncCompleteShutdownBlocks();
+    VERIFY_ASYNC_SHUTDOWN_IN_PROGRESS();
 
     // Run the task to unblock shutdown.
     DispatchAndRunTaskWithTracker(std::move(task), GetParam());
@@ -382,7 +400,7 @@
     // shutdown after shutdown because Shutdown() won't return if there are
     // pending BLOCK_SHUTDOWN tasks.
   } else {
-    tracker_.CompleteShutdown();
+    WAIT_FOR_ASYNC_SHUTDOWN_COMPLETED();
 
     // The task shouldn't be allowed to run after shutdown.
     DispatchAndRunTaskWithTracker(std::move(task), GetParam());
@@ -397,8 +415,9 @@
   EXPECT_TRUE(tracker_.WillPostTask(&block_shutdown_task,
                                     TaskShutdownBehavior::BLOCK_SHUTDOWN));
 
-  // Start shutdown.
-  tracker_.StartShutdown();
+  // Call Shutdown() asynchronously.
+  CallShutdownAsync();
+  VERIFY_ASYNC_SHUTDOWN_IN_PROGRESS();
 
   if (GetParam() == TaskShutdownBehavior::BLOCK_SHUTDOWN) {
     // Inform |task_tracker_| that a BLOCK_SHUTDOWN task will be posted.
@@ -417,10 +436,8 @@
     // Don't try to run the task, because it wasn't allowed to be posted.
   }
 
-  // Verify that CompleteShutdown() blocks.
-  ExpectAsyncCompleteShutdownBlocks();
-
   // Unblock shutdown by running |block_shutdown_task|.
+  VERIFY_ASYNC_SHUTDOWN_IN_PROGRESS();
   DispatchAndRunTaskWithTracker(std::move(block_shutdown_task),
                                 TaskShutdownBehavior::BLOCK_SHUTDOWN);
   EXPECT_EQ(GetParam() == TaskShutdownBehavior::BLOCK_SHUTDOWN ? 2U : 1U,
@@ -429,7 +446,7 @@
 }
 
 TEST_P(TaskSchedulerTaskTrackerTest, WillPostAfterShutdown) {
-  test::ShutdownTaskTracker(&tracker_);
+  tracker_.Shutdown();
 
   Task task(CreateTask());
 
@@ -509,10 +526,13 @@
   EXPECT_FALSE(ThreadTaskRunnerHandle::IsSet());
   EXPECT_FALSE(SequencedTaskRunnerHandle::IsSet());
 
+  testing::StrictMock<MockCanScheduleSequenceObserver> never_notified_observer;
   auto sequence = test::CreateSequenceWithTask(
       std::move(verify_task), traits, std::move(task_runner), execution_mode);
 
-  tracker->RunAndPopNextTask(std::move(sequence));
+  ASSERT_TRUE(tracker->WillScheduleSequence(sequence->BeginTransaction(),
+                                            &never_notified_observer));
+  tracker->RunAndPopNextTask(std::move(sequence), &never_notified_observer);
 
   // TaskRunnerHandle state is reset outside of task's scope.
   EXPECT_FALSE(ThreadTaskRunnerHandle::IsSet());
@@ -752,7 +772,7 @@
 
   // Shutdown() should return immediately since there are no pending
   // BLOCK_SHUTDOWN tasks.
-  test::ShutdownTaskTracker(&tracker_);
+  tracker_.Shutdown();
 
   // FlushForTesting() should return immediately after shutdown, even if an
   // undelayed task hasn't run.
@@ -769,7 +789,7 @@
 
   // Shutdown() should return immediately since there are no pending
   // BLOCK_SHUTDOWN tasks.
-  test::ShutdownTaskTracker(&tracker_);
+  tracker_.Shutdown();
 
   // FlushForTesting() should callback immediately after shutdown, even if an
   // undelayed task hasn't run.
@@ -796,7 +816,7 @@
 
   // Shutdown() should return immediately since there are no pending
   // BLOCK_SHUTDOWN tasks.
-  test::ShutdownTaskTracker(&tracker_);
+  tracker_.Shutdown();
 
   // FlushForTesting() should now return, even if an undelayed task hasn't run.
   WAIT_FOR_ASYNC_FLUSH_RETURNED();
@@ -820,7 +840,7 @@
 
   // Shutdown() should return immediately since there are no pending
   // BLOCK_SHUTDOWN tasks.
-  test::ShutdownTaskTracker(&tracker_);
+  tracker_.Shutdown();
 
   // FlushAsyncForTesting() should now callback, even if an undelayed task
   // hasn't run.
@@ -849,7 +869,7 @@
 
   // Since the delayed task doesn't block shutdown, a call to Shutdown() should
   // not hang.
-  test::ShutdownTaskTracker(&tracker_);
+  tracker_.Shutdown();
 }
 
 INSTANTIATE_TEST_SUITE_P(
@@ -888,9 +908,11 @@
     sequence_transaction.PushTask(std::move(task));
 
     EXPECT_FALSE(SequenceToken::GetForCurrentThread().IsValid());
+    ASSERT_TRUE(tracker_.WillScheduleSequence(sequence_transaction,
+                                              &never_notified_observer_));
   }
 
-  tracker_.RunAndPopNextTask(std::move(sequence));
+  tracker_.RunAndPopNextTask(std::move(sequence), &never_notified_observer_);
   EXPECT_FALSE(SequenceToken::GetForCurrentThread().IsValid());
 }
 
@@ -925,7 +947,7 @@
   EXPECT_EQ(kLoadTestNumIterations * 3, NumTasksExecuted());
 
   // Should return immediately because no tasks are blocking shutdown.
-  test::ShutdownTaskTracker(&tracker_);
+  tracker_.Shutdown();
 }
 
 TEST_F(TaskSchedulerTaskTrackerTest,
@@ -968,9 +990,8 @@
   for (const auto& thread : post_threads)
     thread->Join();
 
-  // Start shutdown and try to complete shutdown asynchronously.
-  tracker_.StartShutdown();
-  ExpectAsyncCompleteShutdownBlocks();
+  // Call Shutdown() asynchronously.
+  CallShutdownAsync();
 
   // Run tasks asynchronously.
   std::vector<std::unique_ptr<ThreadPostingAndRunningTask>> run_threads;
@@ -1009,9 +1030,8 @@
   EXPECT_TRUE(tracker_.WillPostTask(&block_shutdown_task,
                                     TaskShutdownBehavior::BLOCK_SHUTDOWN));
 
-  // Start shutdown and try to complete it asynchronously.
-  tracker_.StartShutdown();
-  ExpectAsyncCompleteShutdownBlocks();
+  // Call Shutdown() asynchronously.
+  CallShutdownAsync();
 
   // Post and run tasks asynchronously.
   std::vector<std::unique_ptr<ThreadPostingAndRunningTask>> threads;
@@ -1067,8 +1087,335 @@
 
   scoped_refptr<Sequence> sequence =
       test::CreateSequenceWithTask(std::move(task_1), default_traits);
-  sequence->BeginTransaction().PushTask(std::move(task_2));
-  EXPECT_EQ(sequence, tracker_.RunAndPopNextTask(sequence));
+  {
+    Sequence::Transaction sequence_transaction(sequence->BeginTransaction());
+    sequence_transaction.PushTask(std::move(task_2));
+    EXPECT_TRUE(tracker_.WillScheduleSequence(sequence_transaction, nullptr));
+  }
+
+  EXPECT_EQ(sequence, tracker_.RunAndPopNextTask(sequence, nullptr));
+}
+
+namespace {
+
+void TestWillScheduleBestEffortSequenceWithMaxBestEffortSequences(
+    int max_num_scheduled_best_effort_sequences,
+    TaskTracker& tracker) {
+  // Simulate posting |max_num_scheduled_best_effort_sequences| best-effort
+  // tasks and scheduling the associated sequences. This should succeed.
+  std::vector<scoped_refptr<Sequence>> scheduled_sequences;
+  testing::StrictMock<MockCanScheduleSequenceObserver> never_notified_observer;
+  TaskTraits best_effort_traits = TaskTraits(TaskPriority::BEST_EFFORT);
+  for (int i = 0; i < max_num_scheduled_best_effort_sequences; ++i) {
+    Task task(FROM_HERE, DoNothing(), TimeDelta());
+    EXPECT_TRUE(
+        tracker.WillPostTask(&task, best_effort_traits.shutdown_behavior()));
+    scoped_refptr<Sequence> sequence =
+        test::CreateSequenceWithTask(std::move(task), best_effort_traits);
+    ASSERT_TRUE(tracker.WillScheduleSequence(sequence->BeginTransaction(),
+                                             &never_notified_observer));
+    scheduled_sequences.push_back(std::move(sequence));
+  }
+
+  // Simulate posting extra best-effort tasks and scheduling the associated
+  // sequences. This should fail because the maximum number of best-effort
+  // sequences that can be scheduled concurrently is already reached.
+  std::vector<std::unique_ptr<bool>> extra_tasks_did_run;
+  std::vector<
+      std::unique_ptr<testing::StrictMock<MockCanScheduleSequenceObserver>>>
+      extra_observers;
+  std::vector<scoped_refptr<Sequence>> extra_sequences;
+  for (int i = 0; i < max_num_scheduled_best_effort_sequences; ++i) {
+    extra_tasks_did_run.push_back(std::make_unique<bool>());
+    Task extra_task(
+        FROM_HERE,
+        BindOnce([](bool* extra_task_did_run) { *extra_task_did_run = true; },
+                 Unretained(extra_tasks_did_run.back().get())),
+        TimeDelta());
+    EXPECT_TRUE(tracker.WillPostTask(&extra_task,
+                                     best_effort_traits.shutdown_behavior()));
+    extra_sequences.push_back(test::CreateSequenceWithTask(
+        std::move(extra_task), best_effort_traits));
+    extra_observers.push_back(
+        std::make_unique<
+            testing::StrictMock<MockCanScheduleSequenceObserver>>());
+    EXPECT_FALSE(
+        tracker.WillScheduleSequence(extra_sequences.back()->BeginTransaction(),
+                                     extra_observers.back().get()));
+  }
+
+  // Run the sequences scheduled at the beginning of the test. Expect an
+  // observer from |extra_observer| to be notified every time a task finishes to
+  // run.
+  for (int i = 0; i < max_num_scheduled_best_effort_sequences; ++i) {
+    EXPECT_CALL(*extra_observers[i].get(),
+                MockOnCanScheduleSequence(extra_sequences[i].get()));
+    EXPECT_FALSE(tracker.RunAndPopNextTask(scheduled_sequences[i],
+                                           &never_notified_observer));
+    testing::Mock::VerifyAndClear(extra_observers[i].get());
+  }
+
+  // Run the extra sequences.
+  for (int i = 0; i < max_num_scheduled_best_effort_sequences; ++i) {
+    EXPECT_FALSE(*extra_tasks_did_run[i]);
+    EXPECT_FALSE(tracker.RunAndPopNextTask(extra_sequences[i],
+                                           &never_notified_observer));
+    EXPECT_TRUE(*extra_tasks_did_run[i]);
+  }
+}
+
+}  // namespace
+
+// Verify that WillScheduleSequence() returns nullptr when it receives a
+// best-effort sequence and the maximum number of best-effort sequences that can
+// be scheduled concurrently is reached. Verify that an observer is notified
+// when a best-effort sequence can be scheduled (i.e. when one of the previously
+// scheduled best-effort sequences has run).
+TEST_F(TaskSchedulerTaskTrackerTest,
+       WillScheduleBestEffortSequenceWithMaxBestEffortSequences) {
+  constexpr int kMaxNumScheduledBestEffortSequences = 2;
+  TaskTracker tracker("Test", kMaxNumScheduledBestEffortSequences);
+  TestWillScheduleBestEffortSequenceWithMaxBestEffortSequences(
+      kMaxNumScheduledBestEffortSequences, tracker);
+}
+
+// Verify that providing a cap for the number of BEST_EFFORT tasks to the
+// constructor of TaskTracker is compatible with using an execution fence.
+TEST_F(TaskSchedulerTaskTrackerTest,
+       WillScheduleBestEffortSequenceWithMaxBestEffortSequencesAndFence) {
+  constexpr int kMaxNumScheduledBestEffortSequences = 2;
+  TaskTracker tracker("Test", kMaxNumScheduledBestEffortSequences);
+  tracker.SetExecutionFenceEnabled(true);
+  tracker.SetExecutionFenceEnabled(false);
+  TestWillScheduleBestEffortSequenceWithMaxBestEffortSequences(
+      kMaxNumScheduledBestEffortSequences, tracker);
+}
+
+namespace {
+
+void SetBool(bool* arg) {
+  ASSERT_TRUE(arg);
+  EXPECT_FALSE(*arg);
+  *arg = true;
+}
+
+}  // namespace
+
+// Verify that enabling the ScopedExecutionFence will prevent
+// WillScheduleSequence() returning sequence.
+TEST_F(TaskSchedulerTaskTrackerTest,
+       WillScheduleSequenceWithScopedExecutionFence) {
+  TaskTraits default_traits = {};
+  Task task_a(FROM_HERE, DoNothing(), TimeDelta());
+  EXPECT_TRUE(
+      tracker_.WillPostTask(&task_a, default_traits.shutdown_behavior()));
+  scoped_refptr<Sequence> sequence_a =
+      test::CreateSequenceWithTask(std::move(task_a), default_traits);
+  testing::StrictMock<MockCanScheduleSequenceObserver> never_notified_observer;
+  EXPECT_TRUE(tracker_.WillScheduleSequence(sequence_a->BeginTransaction(),
+                                            &never_notified_observer));
+
+  // Verify that WillScheduleSequence() returns nullptr for foreground sequence
+  // when the ScopedExecutionFence is enabled.
+  tracker_.SetExecutionFenceEnabled(true);
+  bool task_b_1_did_run = false;
+  Task task_b_1(FROM_HERE, BindOnce(&SetBool, Unretained(&task_b_1_did_run)),
+                TimeDelta());
+  EXPECT_TRUE(
+      tracker_.WillPostTask(&task_b_1, default_traits.shutdown_behavior()));
+  scoped_refptr<Sequence> sequence_b =
+      test::CreateSequenceWithTask(std::move(task_b_1), default_traits);
+  testing::StrictMock<MockCanScheduleSequenceObserver> observer_b_1;
+  EXPECT_EQ(0u, tracker_.GetPreemptedSequenceCountForTesting(
+                    TaskPriority::BEST_EFFORT));
+  EXPECT_FALSE(tracker_.WillScheduleSequence(sequence_b->BeginTransaction(),
+                                             &observer_b_1));
+  EXPECT_EQ(1u, tracker_.GetPreemptedSequenceCountForTesting(
+                    TaskPriority::USER_VISIBLE));
+
+  bool task_b_2_did_run = false;
+  Task task_b_2(FROM_HERE, BindOnce(&SetBool, Unretained(&task_b_2_did_run)),
+                TimeDelta());
+  TaskTraits best_effort_traits = TaskTraits(TaskPriority::BEST_EFFORT);
+  EXPECT_TRUE(
+      tracker_.WillPostTask(&task_b_2, best_effort_traits.shutdown_behavior()));
+  sequence_b->BeginTransaction().PushTask(std::move(task_b_2));
+  testing::StrictMock<MockCanScheduleSequenceObserver> observer_b_2;
+  EXPECT_FALSE(tracker_.WillScheduleSequence(sequence_b->BeginTransaction(),
+                                             &observer_b_2));
+  // The TaskPriority of the Sequence is unchanged by posting new tasks to it.
+  EXPECT_EQ(2u, tracker_.GetPreemptedSequenceCountForTesting(
+                    TaskPriority::USER_VISIBLE));
+
+  // Verify that WillScheduleSequence() returns nullptr for best-effort sequence
+  // when the ScopedExecutionFence is enabled.
+  bool task_c_did_run = false;
+  Task task_c(FROM_HERE, BindOnce(&SetBool, Unretained(&task_c_did_run)),
+              TimeDelta());
+  EXPECT_TRUE(
+      tracker_.WillPostTask(&task_c, best_effort_traits.shutdown_behavior()));
+  scoped_refptr<Sequence> sequence_c =
+      test::CreateSequenceWithTask(std::move(task_c), best_effort_traits);
+  testing::StrictMock<MockCanScheduleSequenceObserver> observer_c;
+  EXPECT_FALSE(tracker_.WillScheduleSequence(sequence_c->BeginTransaction(),
+                                             &observer_c));
+  EXPECT_EQ(1u, tracker_.GetPreemptedSequenceCountForTesting(
+                    TaskPriority::BEST_EFFORT));
+
+  // Verifies that the sequences preempted when the fence is on are rescheduled
+  // right after the fence is released.
+  EXPECT_CALL(observer_b_1, MockOnCanScheduleSequence(sequence_b.get()));
+  EXPECT_CALL(observer_b_2, MockOnCanScheduleSequence(sequence_b.get()));
+  EXPECT_CALL(observer_c, MockOnCanScheduleSequence(sequence_c.get()));
+  tracker_.SetExecutionFenceEnabled(false);
+  testing::Mock::VerifyAndClear(&observer_b_1);
+  testing::Mock::VerifyAndClear(&observer_b_2);
+  testing::Mock::VerifyAndClear(&observer_c);
+  EXPECT_EQ(0u, tracker_.GetPreemptedSequenceCountForTesting(
+                    TaskPriority::USER_VISIBLE));
+  EXPECT_EQ(0u, tracker_.GetPreemptedSequenceCountForTesting(
+                    TaskPriority::BEST_EFFORT));
+
+  // Runs the sequences and verifies the tasks are done.
+  EXPECT_FALSE(
+      tracker_.RunAndPopNextTask(sequence_a, &never_notified_observer));
+
+  EXPECT_FALSE(task_b_1_did_run);
+  EXPECT_EQ(sequence_b, tracker_.RunAndPopNextTask(sequence_b, &observer_b_1));
+  EXPECT_TRUE(task_b_1_did_run);
+
+  EXPECT_FALSE(task_b_2_did_run);
+  EXPECT_FALSE(tracker_.RunAndPopNextTask(sequence_b, &observer_b_2));
+  EXPECT_TRUE(task_b_2_did_run);
+
+  EXPECT_FALSE(task_c_did_run);
+  EXPECT_FALSE(tracker_.RunAndPopNextTask(sequence_c, &observer_c));
+  EXPECT_TRUE(task_c_did_run);
+
+  // Verify that WillScheduleSequence() returns the sequence when the
+  // ScopedExecutionFence isn't enabled.
+  Task task_d(FROM_HERE, DoNothing(), TimeDelta());
+  EXPECT_TRUE(
+      tracker_.WillPostTask(&task_d, default_traits.shutdown_behavior()));
+  scoped_refptr<Sequence> sequence_d =
+      test::CreateSequenceWithTask(std::move(task_d), default_traits);
+  EXPECT_TRUE(tracker_.WillScheduleSequence(sequence_d->BeginTransaction(),
+                                            &never_notified_observer));
+}
+
+// Verify that RunAndPopNextTask() doesn't reschedule the best-effort sequence
+// it was assigned if there is a preempted best-effort sequence with an earlier
+// sequence time (compared to the next task in the sequence assigned to
+// RunAndPopNextTask()).
+TEST_F(TaskSchedulerTaskTrackerTest,
+       RunNextBestEffortTaskWithEarlierPendingBestEffortTask) {
+  constexpr int kMaxNumScheduledBestEffortSequences = 1;
+  TaskTracker tracker("Test", kMaxNumScheduledBestEffortSequences);
+  testing::StrictMock<MockCanScheduleSequenceObserver> never_notified_observer;
+  TaskTraits best_effort_traits = TaskTraits(TaskPriority::BEST_EFFORT);
+
+  // Simulate posting a best-effort task and scheduling the associated sequence.
+  // This should succeed.
+  bool task_a_1_did_run = false;
+  Task task_a_1(FROM_HERE, BindOnce(&SetBool, Unretained(&task_a_1_did_run)),
+                TimeDelta());
+  EXPECT_TRUE(
+      tracker.WillPostTask(&task_a_1, best_effort_traits.shutdown_behavior()));
+  scoped_refptr<Sequence> sequence_a =
+      test::CreateSequenceWithTask(std::move(task_a_1), best_effort_traits);
+  EXPECT_TRUE(tracker.WillScheduleSequence(sequence_a->BeginTransaction(),
+                                           &never_notified_observer));
+
+  // Simulate posting an extra best-effort task and scheduling the associated
+  // sequence. This should fail because the maximum number of best-effort
+  // sequences that can be scheduled concurrently is already reached.
+  bool task_b_1_did_run = false;
+  Task task_b_1(FROM_HERE, BindOnce(&SetBool, Unretained(&task_b_1_did_run)),
+                TimeDelta());
+  EXPECT_TRUE(
+      tracker.WillPostTask(&task_b_1, best_effort_traits.shutdown_behavior()));
+  scoped_refptr<Sequence> sequence_b =
+      test::CreateSequenceWithTask(std::move(task_b_1), best_effort_traits);
+  testing::StrictMock<MockCanScheduleSequenceObserver> task_b_1_observer;
+  EXPECT_FALSE(tracker.WillScheduleSequence(sequence_b->BeginTransaction(),
+                                            &task_b_1_observer));
+
+  // Wait to be sure that the sequence time of |task_a_2| is after the sequenced
+  // time of |task_b_1|.
+  PlatformThread::Sleep(TestTimeouts::tiny_timeout());
+
+  // Post an extra best-effort task in |sequence_a|.
+  bool task_a_2_did_run = false;
+  Task task_a_2(FROM_HERE, BindOnce(&SetBool, Unretained(&task_a_2_did_run)),
+                TimeDelta());
+  EXPECT_TRUE(
+      tracker.WillPostTask(&task_a_2, best_effort_traits.shutdown_behavior()));
+  sequence_a->BeginTransaction().PushTask(std::move(task_a_2));
+
+  // Run the first task in |sequence_a|. RunAndPopNextTask() should return
+  // nullptr since |sequence_a| can't be rescheduled immediately.
+  // |task_b_1_observer| should be notified that |sequence_b| can be scheduled.
+  testing::StrictMock<MockCanScheduleSequenceObserver> task_a_2_observer;
+  EXPECT_CALL(task_b_1_observer, MockOnCanScheduleSequence(sequence_b.get()));
+  EXPECT_FALSE(tracker.RunAndPopNextTask(sequence_a, &task_a_2_observer));
+  testing::Mock::VerifyAndClear(&task_b_1_observer);
+  EXPECT_TRUE(task_a_1_did_run);
+
+  // Run the first task in |sequence_b|. RunAndPopNextTask() should return
+  // nullptr since |sequence_b| is empty after popping a task from it.
+  // |task_a_2_observer| should be notified that |sequence_a| can be
+  // scheduled.
+  EXPECT_CALL(task_a_2_observer, MockOnCanScheduleSequence(sequence_a.get()));
+  EXPECT_FALSE(tracker.RunAndPopNextTask(sequence_b, &never_notified_observer));
+  testing::Mock::VerifyAndClear(&task_a_2_observer);
+  EXPECT_TRUE(task_b_1_did_run);
+
+  // Run the first task in |sequence_a|. RunAndPopNextTask() should return
+  // nullptr since |sequence_b| is empty after popping a task from it. No
+  // observer should be notified.
+  EXPECT_FALSE(tracker.RunAndPopNextTask(sequence_a, &never_notified_observer));
+  EXPECT_TRUE(task_a_2_did_run);
+}
+
+// Verify that preempted best-effort sequences are scheduled when shutdown
+// starts.
+TEST_F(TaskSchedulerTaskTrackerTest,
+       SchedulePreemptedBestEffortSequencesOnShutdown) {
+  constexpr int kMaxNumScheduledBestEffortSequences = 0;
+  TaskTracker tracker("Test", kMaxNumScheduledBestEffortSequences);
+  testing::StrictMock<MockCanScheduleSequenceObserver> observer;
+
+  // Simulate scheduling sequences. TaskTracker should prevent this.
+  std::vector<scoped_refptr<Sequence>> preempted_sequences;
+  for (int i = 0; i < 3; ++i) {
+    Task task(FROM_HERE, DoNothing(),
+              TimeDelta());
+    EXPECT_TRUE(
+        tracker.WillPostTask(&task, TaskShutdownBehavior::BLOCK_SHUTDOWN));
+    scoped_refptr<Sequence> sequence = test::CreateSequenceWithTask(
+        std::move(task), TaskTraits(TaskPriority::BEST_EFFORT,
+                                    TaskShutdownBehavior::BLOCK_SHUTDOWN));
+    EXPECT_FALSE(
+        tracker.WillScheduleSequence(sequence->BeginTransaction(), &observer));
+    preempted_sequences.push_back(std::move(sequence));
+
+    // Wait to be sure that tasks have different |sequenced_time|.
+    PlatformThread::Sleep(TestTimeouts::tiny_timeout());
+  }
+
+  // Perform shutdown. Expect |preempted_sequences| to be scheduled in posting
+  // order.
+  {
+    testing::InSequence in_sequence;
+    for (auto& preempted_sequence : preempted_sequences) {
+      EXPECT_CALL(observer, MockOnCanScheduleSequence(preempted_sequence.get()))
+          .WillOnce(testing::Invoke([&tracker](Sequence* sequence) {
+            // Run the task to unblock shutdown.
+            tracker.RunAndPopNextTask(sequence, nullptr);
+          }));
+    }
+    tracker.Shutdown();
+  }
 }
 
 namespace {
@@ -1092,10 +1439,15 @@
     TaskTraits default_traits = {};
     EXPECT_TRUE(task_tracker->WillPostTask(&task_without_sync_primitives,
                                            default_traits.shutdown_behavior()));
+    testing::StrictMock<MockCanScheduleSequenceObserver>
+        never_notified_observer;
     auto sequence_without_sync_primitives = test::CreateSequenceWithTask(
         std::move(task_without_sync_primitives), default_traits);
-    task_tracker->RunAndPopNextTask(
-        std::move(sequence_without_sync_primitives));
+    ASSERT_TRUE(task_tracker->WillScheduleSequence(
+        sequence_without_sync_primitives->BeginTransaction(),
+        &never_notified_observer));
+    task_tracker->RunAndPopNextTask(std::move(sequence_without_sync_primitives),
+                                    &never_notified_observer);
 
     // Disallow waiting. Expect TaskTracker to allow it before running a task
     // with the WithBaseSyncPrimitives() trait.
@@ -1113,7 +1465,11 @@
         traits_with_sync_primitives.shutdown_behavior()));
     auto sequence_with_sync_primitives = test::CreateSequenceWithTask(
         std::move(task_with_sync_primitives), traits_with_sync_primitives);
-    task_tracker->RunAndPopNextTask(std::move(sequence_with_sync_primitives));
+    ASSERT_TRUE(task_tracker->WillScheduleSequence(
+        sequence_with_sync_primitives->BeginTransaction(),
+        &never_notified_observer));
+    task_tracker->RunAndPopNextTask(std::move(sequence_with_sync_primitives),
+                                    &never_notified_observer);
 
     ScopedAllowBaseSyncPrimitivesForTesting
         allow_wait_in_task_tracker_destructor;
@@ -1143,6 +1499,7 @@
   auto statistics_recorder = StatisticsRecorder::CreateTemporaryForTesting();
 
   TaskTracker tracker("Test");
+  testing::StrictMock<MockCanScheduleSequenceObserver> never_notified_observer;
 
   struct {
     const TaskTraits traits;
@@ -1182,8 +1539,10 @@
 
     HistogramTester tester;
 
-    tracker.RunAndPopNextTask(
-        test::CreateSequenceWithTask(std::move(task), test.traits));
+    auto sequence = test::CreateSequenceWithTask(std::move(task), test.traits);
+    ASSERT_TRUE(tracker.WillScheduleSequence(sequence->BeginTransaction(),
+                                             &never_notified_observer));
+    tracker.RunAndPopNextTask(std::move(sequence), &never_notified_observer);
     tester.ExpectTotalCount(test.expected_histogram, 1);
   }
 }
diff --git a/base/task/task_scheduler/test_utils.cc b/base/task/task_scheduler/test_utils.cc
index f7f04bd..c9e2e57 100644
--- a/base/task/task_scheduler/test_utils.cc
+++ b/base/task/task_scheduler/test_utils.cc
@@ -58,8 +58,9 @@
 
 scoped_refptr<TaskRunner> CreateTaskRunnerWithExecutionMode(
     test::ExecutionMode execution_mode,
-    MockSchedulerTaskRunnerDelegate* mock_scheduler_task_runner_delegate,
-    const TaskTraits& traits) {
+    MockSchedulerTaskRunnerDelegate* mock_scheduler_task_runner_delegate) {
+  // Allow tasks posted to the returned TaskRunner to wait on a WaitableEvent.
+  const TaskTraits traits = {WithBaseSyncPrimitives()};
   switch (execution_mode) {
     case test::ExecutionMode::PARALLEL:
       return CreateTaskRunnerWithTraits(traits,
@@ -162,11 +163,6 @@
   worker_pool_ = worker_pool;
 }
 
-void ShutdownTaskTracker(TaskTracker* task_tracker) {
-  task_tracker->StartShutdown();
-  task_tracker->CompleteShutdown();
-}
-
 }  // namespace test
 }  // namespace internal
 }  // namespace base
diff --git a/base/task/task_scheduler/test_utils.h b/base/task/task_scheduler/test_utils.h
index 97b80c3..bd82a22 100644
--- a/base/task/task_scheduler/test_utils.h
+++ b/base/task/task_scheduler/test_utils.h
@@ -98,8 +98,7 @@
 // Caveat: this does not support ExecutionMode::SINGLE_THREADED.
 scoped_refptr<TaskRunner> CreateTaskRunnerWithExecutionMode(
     test::ExecutionMode execution_mode,
-    MockSchedulerTaskRunnerDelegate* mock_scheduler_task_runner_delegate,
-    const TaskTraits& traits = TaskTraits());
+    MockSchedulerTaskRunnerDelegate* mock_scheduler_task_runner_delegate);
 
 scoped_refptr<TaskRunner> CreateTaskRunnerWithTraits(
     const TaskTraits& traits,
@@ -111,9 +110,6 @@
 
 void WaitWithoutBlockingObserver(WaitableEvent* event);
 
-// Calls StartShutdown() and CompleteShutdown() on |task_tracker|.
-void ShutdownTaskTracker(TaskTracker* task_tracker);
-
 }  // namespace test
 }  // namespace internal
 }  // namespace base
diff --git a/base/test/task_scheduler_test_helpers_android.cc b/base/test/task_scheduler_test_helpers_android.cc
index 2e935a0..99471fda25 100644
--- a/base/test/task_scheduler_test_helpers_android.cc
+++ b/base/test/task_scheduler_test_helpers_android.cc
@@ -19,7 +19,8 @@
 // static
 void TaskSchedulerTestHelpers::SetTaskSchedulerExecutionFenceEnabledForTesting(
     bool execution_fence_enabled) {
-  TaskScheduler::GetInstance()->SetCanRun(!execution_fence_enabled);
+  TaskScheduler::GetInstance()->SetExecutionFenceEnabled(
+      execution_fence_enabled);
 }
 
 }  // namespace base
diff --git a/build/fuchsia/linux.sdk.sha1 b/build/fuchsia/linux.sdk.sha1
index c4393ff..b4c72e2 100644
--- a/build/fuchsia/linux.sdk.sha1
+++ b/build/fuchsia/linux.sdk.sha1
@@ -1 +1 @@
-8916765319702380096
\ No newline at end of file
+8916737669163559168
\ No newline at end of file
diff --git a/build/fuchsia/mac.sdk.sha1 b/build/fuchsia/mac.sdk.sha1
index 5a0d165..5a44181d 100644
--- a/build/fuchsia/mac.sdk.sha1
+++ b/build/fuchsia/mac.sdk.sha1
@@ -1 +1 @@
-8916772086441183392
\ No newline at end of file
+8916742152396029840
\ No newline at end of file
diff --git a/build/util/android_chrome_version.py b/build/util/android_chrome_version.py
index f183441..5628f1a 100644
--- a/build/util/android_chrome_version.py
+++ b/build/util/android_chrome_version.py
@@ -113,11 +113,11 @@
 """
 ARCH64_APK_VARIANTS = {
     '64_32': {
-        'PACKAGES': frozenset(['MONOCHROME']),
+        'PACKAGES': frozenset(['MONOCHROME', 'TRICHROME']),
         'MODIFIER': 10
     },
     '64': {
-        'PACKAGES': frozenset(['MONOCHROME']),
+        'PACKAGES': frozenset(['MONOCHROME', 'TRICHROME']),
         'MODIFIER': 20
     }
 }
diff --git a/build/util/android_chrome_version_test.py b/build/util/android_chrome_version_test.py
index ad3c5ca1..5e743d3 100644
--- a/build/util/android_chrome_version_test.py
+++ b/build/util/android_chrome_version_test.py
@@ -172,9 +172,13 @@
         self.EXAMPLE_VERSION_VALUES, arch='arm64', is_next_build=False)
     arch_monochrome_64_32_version_code = output['MONOCHROME_64_32_VERSION_CODE']
     arch_monochrome_64_version_code = output['MONOCHROME_64_VERSION_CODE']
+    arch_trichrome_64_32_version_code = output['TRICHROME_64_32_VERSION_CODE']
+    arch_trichrome_64_version_code = output['TRICHROME_64_VERSION_CODE']
 
     self.assertEqual(arch_monochrome_64_32_version_code, '372000042')
     self.assertEqual(arch_monochrome_64_version_code, '372000052')
+    self.assertEqual(arch_trichrome_64_32_version_code, '372000043')
+    self.assertEqual(arch_trichrome_64_version_code, '372000053')
 
   def testGenerateVersionCodesAndroidArchX64(self):
     """Assert it handles different architectures correctly.
@@ -200,9 +204,13 @@
         self.EXAMPLE_VERSION_VALUES, arch='x64', is_next_build=False)
     arch_monochrome_64_32_version_code = output['MONOCHROME_64_32_VERSION_CODE']
     arch_monochrome_64_version_code = output['MONOCHROME_64_VERSION_CODE']
+    arch_trichrome_64_32_version_code = output['TRICHROME_64_32_VERSION_CODE']
+    arch_trichrome_64_version_code = output['TRICHROME_64_VERSION_CODE']
 
     self.assertEqual(arch_monochrome_64_32_version_code, '372000072')
     self.assertEqual(arch_monochrome_64_version_code, '372000082')
+    self.assertEqual(arch_trichrome_64_32_version_code, '372000073')
+    self.assertEqual(arch_trichrome_64_version_code, '372000083')
 
   def testGenerateVersionCodesAndroidArchOrderArm(self):
     """Assert it handles different architectures correctly.
diff --git a/build/util/version.gni b/build/util/version.gni
index 0bcae72..a28f2e46 100644
--- a/build/util/version.gni
+++ b/build/util/version.gni
@@ -51,7 +51,9 @@
   if (target_cpu == "arm64" || target_cpu == "x64") {
     _version_dictionary_template +=
         "monochrome_64_32_version_code = \"@MONOCHROME_64_32_VERSION_CODE@\" " +
-        "monochrome_64_version_code = \"@MONOCHROME_64_VERSION_CODE@\" "
+        "monochrome_64_version_code = \"@MONOCHROME_64_VERSION_CODE@\" " +
+        "trichrome_64_32_version_code = \"@TRICHROME_64_32_VERSION_CODE@\" " +
+        "trichrome_64_version_code = \"@TRICHROME_64_VERSION_CODE@\" "
   }
 
   _script_arguments += [
@@ -96,16 +98,18 @@
 } else if (target_os == "android") {
   forward_variables_from(_result,
                          [
-                           "chrome_version_code",
                            "chrome_modern_version_code",
-                           "monochrome_version_code",
-                           "trichrome_version_code",
-                           "notouch_chrome_version_code",
-                           "webview_stable_version_code",
-                           "webview_beta_version_code",
-                           "webview_dev_version_code",
+                           "chrome_version_code",
                            "monochrome_64_32_version_code",
                            "monochrome_64_version_code",
+                           "monochrome_version_code",
+                           "notouch_chrome_version_code",
+                           "trichrome_64_32_version_code",
+                           "trichrome_64_version_code",
+                           "trichrome_version_code",
+                           "webview_beta_version_code",
+                           "webview_dev_version_code",
+                           "webview_stable_version_code",
                          ])
 
   chrome_version_name = chrome_version_full
@@ -126,6 +130,8 @@
     lines_to_write += [
       "Monochrome_64_32=$monochrome_64_32_version_code",
       "Monochrome_64=$monochrome_64_version_code",
+      "TrichromeChrome_64_32=$trichrome_64_32_version_code",
+      "TrichromeChrome_64=$trichrome_64_version_code",
     ]
   }
 
diff --git a/build/util/version_test.py b/build/util/version_test.py
index 290d026..2a65ddc7 100644
--- a/build/util/version_test.py
+++ b/build/util/version_test.py
@@ -120,7 +120,9 @@
     new_template = (
         self._EXAMPLE_ANDROID_TEMPLATE +
         "monochrome_64_32_version_code = \"@MONOCHROME_64_32_VERSION_CODE@\" "
-        "monochrome_64_version_code = \"@MONOCHROME_64_VERSION_CODE@\" ")
+        "monochrome_64_version_code = \"@MONOCHROME_64_VERSION_CODE@\" "
+        "trichrome_64_32_version_code = \"@TRICHROME_64_32_VERSION_CODE@\" "
+        "trichrome_64_version_code = \"@TRICHROME_64_VERSION_CODE@\" ")
     args_with_template = _ReplaceArgs(self._EXAMPLE_ANDROID_ARGS,
                                       ['-t', new_template])
     new_args = _ReplaceArgs(args_with_template, ['-a', 'arm64'])
@@ -131,13 +133,19 @@
                              r'\bmonochrome_64_32_version_code = "\d+"\s')
     self.assertRegexpMatches(contents,
                              r'\bmonochrome_64_version_code = "\d+"\s')
+    self.assertRegexpMatches(contents,
+                             r'\btrichrome_64_32_version_code = "\d+"\s')
+    self.assertRegexpMatches(contents,
+                             r'\btrichrome_64_version_code = "\d+"\s')
 
   def testBuildOutputAndroidArchVariantsX64(self):
     """Assert 64-bit-specific version codes"""
     new_template = (
         self._EXAMPLE_ANDROID_TEMPLATE +
         "monochrome_64_32_version_code = \"@MONOCHROME_64_32_VERSION_CODE@\" "
-        "monochrome_64_version_code = \"@MONOCHROME_64_VERSION_CODE@\" ")
+        "monochrome_64_version_code = \"@MONOCHROME_64_VERSION_CODE@\" "
+        "trichrome_64_32_version_code = \"@TRICHROME_64_32_VERSION_CODE@\" "
+        "trichrome_64_version_code = \"@TRICHROME_64_VERSION_CODE@\" ")
     args_with_template = _ReplaceArgs(self._EXAMPLE_ANDROID_ARGS,
                                       ['-t', new_template])
     new_args = _ReplaceArgs(args_with_template, ['-a', 'x64'])
@@ -148,6 +156,10 @@
                              r'\bmonochrome_64_32_version_code = "\d+"\s')
     self.assertRegexpMatches(contents,
                              r'\bmonochrome_64_version_code = "\d+"\s')
+    self.assertRegexpMatches(contents,
+                             r'\btrichrome_64_32_version_code = "\d+"\s')
+    self.assertRegexpMatches(contents,
+                             r'\btrichrome_64_version_code = "\d+"\s')
 
   def testBuildOutputAndroidChromeArchInput(self):
     """Assert it raises an exception when using an invalid architecture input"""
diff --git a/cc/layers/layer.cc b/cc/layers/layer.cc
index 70f0024..54907fe 100644
--- a/cc/layers/layer.cc
+++ b/cc/layers/layer.cc
@@ -47,7 +47,7 @@
       opacity(1.f),
       blend_mode(SkBlendMode::kSrcOver),
       is_root_for_isolated_group(false),
-      hit_testable_without_draws_content(false),
+      hit_testable(false),
       contents_opaque(false),
       is_drawable(false),
       double_sided(true),
@@ -741,15 +741,19 @@
   SetNeedsCommit();
 }
 
-void Layer::SetHitTestableWithoutDrawsContent(bool should_hit_test) {
+void Layer::SetHitTestable(bool should_hit_test) {
   DCHECK(IsPropertyChangeAllowed());
-  if (inputs_.hit_testable_without_draws_content == should_hit_test)
+  if (inputs_.hit_testable == should_hit_test)
     return;
-  inputs_.hit_testable_without_draws_content = should_hit_test;
+  inputs_.hit_testable = should_hit_test;
   SetPropertyTreesNeedRebuild();
   SetNeedsCommit();
 }
 
+bool Layer::HitTestable() const {
+  return inputs_.hit_testable;
+}
+
 void Layer::SetContentsOpaque(bool opaque) {
   DCHECK(IsPropertyChangeAllowed());
   if (inputs_.contents_opaque == opaque)
@@ -1423,8 +1427,7 @@
   layer->SetScrollTreeIndex(scroll_tree_index());
   layer->SetOffsetToTransformParent(offset_to_transform_parent_);
   layer->SetDrawsContent(DrawsContent());
-  layer->SetHitTestableWithoutDrawsContent(
-      hit_testable_without_draws_content());
+  layer->SetHitTestable(HitTestable());
   // subtree_property_changed_ is propagated to all descendants while building
   // property trees. So, it is enough to check it only for the current layer.
   if (subtree_property_changed_)
diff --git a/cc/layers/layer.h b/cc/layers/layer.h
index 826ba6f..8fca418 100644
--- a/cc/layers/layer.h
+++ b/cc/layers/layer.h
@@ -336,15 +336,9 @@
   void SetContentsOpaque(bool opaque);
   bool contents_opaque() const { return inputs_.contents_opaque; }
 
-  // Set or get whether this layer should be a hit test target even if not
-  // visible. Normally if DrawsContent() is false, making the layer not
-  // contribute to the final composited output, the layer will not be eligable
-  // for hit testing since it is invisible. Set this to true to allow the layer
-  // to be hit tested regardless.
-  void SetHitTestableWithoutDrawsContent(bool should_hit_test);
-  bool hit_testable_without_draws_content() const {
-    return inputs_.hit_testable_without_draws_content;
-  }
+  // Set or get whether this layer should be a hit test target
+  void SetHitTestable(bool should_hit_test);
+  bool HitTestable() const;
 
   // Set or gets if this layer is a container for fixed position layers in its
   // subtree. Such layers will be positioned and transformed relative to this
@@ -936,10 +930,8 @@
 
     bool is_root_for_isolated_group : 1;
 
-    // Hit testing depends on draws_content (see: |LayerImpl::should_hit_test|)
-    // and this bit can be set to cause the LayerImpl to be hit testable without
-    // draws_content.
-    bool hit_testable_without_draws_content : 1;
+    // Hit testing depends on this bit.
+    bool hit_testable : 1;
 
     bool contents_opaque : 1;
 
diff --git a/cc/layers/layer_impl.cc b/cc/layers/layer_impl.cc
index 0d9d4fb..36a262b3 100644
--- a/cc/layers/layer_impl.cc
+++ b/cc/layers/layer_impl.cc
@@ -63,7 +63,7 @@
       should_check_backface_visibility_(false),
       draws_content_(false),
       contributes_to_drawn_render_surface_(false),
-      hit_testable_without_draws_content_(false),
+      hit_testable_(false),
       is_resized_by_browser_controls_(false),
       viewport_layer_type_(NOT_VIEWPORT_LAYER),
       background_color_(0),
@@ -324,8 +324,7 @@
   layer->use_parent_backface_visibility_ = use_parent_backface_visibility_;
   layer->should_check_backface_visibility_ = should_check_backface_visibility_;
   layer->draws_content_ = draws_content_;
-  layer->hit_testable_without_draws_content_ =
-      hit_testable_without_draws_content_;
+  layer->hit_testable_ = hit_testable_;
   layer->non_fast_scrollable_region_ = non_fast_scrollable_region_;
   layer->touch_action_region_ = touch_action_region_;
   layer->wheel_event_handler_region_ = wheel_event_handler_region_;
@@ -413,8 +412,7 @@
   result->Set("Transform", std::move(list));
 
   result->SetBoolean("DrawsContent", draws_content_);
-  result->SetBoolean("HitTestableWithoutDrawsContent",
-                     hit_testable_without_draws_content_);
+  result->SetBoolean("HitTestable", hit_testable_);
   result->SetBoolean("Is3dSorted", Is3dSorted());
   result->SetDouble("Opacity", Opacity());
   result->SetBoolean("ContentsOpaque", contents_opaque_);
@@ -607,20 +605,25 @@
   NoteLayerPropertyChanged();
 }
 
-void LayerImpl::SetHitTestableWithoutDrawsContent(bool should_hit_test) {
-  if (hit_testable_without_draws_content_ == should_hit_test)
+void LayerImpl::SetHitTestable(bool should_hit_test) {
+  if (hit_testable_ == should_hit_test)
     return;
 
-  hit_testable_without_draws_content_ = should_hit_test;
+  hit_testable_ = should_hit_test;
   NoteLayerPropertyChanged();
 }
 
-bool LayerImpl::ShouldHitTest() const {
-  bool should_hit_test = draws_content_;
-  if (GetEffectTree().Node(effect_tree_index()))
-    should_hit_test &=
-        !GetEffectTree().Node(effect_tree_index())->subtree_hidden;
-  should_hit_test |= hit_testable_without_draws_content_;
+bool LayerImpl::HitTestable() const {
+  EffectTree& effect_tree = GetEffectTree();
+  bool should_hit_test = hit_testable_;
+  // TODO(sunxd): remove or refactor SetHideLayerAndSubtree, or move this logic
+  // to subclasses of Layer. See https://crbug.com/595843 and
+  // https://crbug.com/931865.
+  // The bit |subtree_hidden| can only be true for ui::Layers. Other layers are
+  // not supposed to set this bit.
+  if (effect_tree.Node(effect_tree_index())) {
+    should_hit_test &= !effect_tree.Node(effect_tree_index())->subtree_hidden;
+  }
   return should_hit_test;
 }
 
diff --git a/cc/layers/layer_impl.h b/cc/layers/layer_impl.h
index 8e6dd6c7..b98cfd6 100644
--- a/cc/layers/layer_impl.h
+++ b/cc/layers/layer_impl.h
@@ -165,15 +165,9 @@
   void SetDrawsContent(bool draws_content);
   bool DrawsContent() const { return draws_content_; }
 
-  // Make the layer hit test (see: |should_hit_test|) even if !draws_content_.
-  void SetHitTestableWithoutDrawsContent(bool should_hit_test);
-  bool hit_testable_without_draws_content() const {
-    return hit_testable_without_draws_content_;
-  }
-
-  // True if either the layer draws content or has been marked as hit testable
-  // without draws_content.
-  bool ShouldHitTest() const;
+  // Make the layer hit testable.
+  void SetHitTestable(bool should_hit_test);
+  bool HitTestable() const;
 
   LayerImplTestProperties* test_properties() {
     if (!test_properties_)
@@ -536,10 +530,8 @@
   bool draws_content_ : 1;
   bool contributes_to_drawn_render_surface_ : 1;
 
-  // Hit testing depends on draws_content (see: |LayerImpl::should_hit_test|)
-  // and this bit can be set to cause the layer to be hit testable without
-  // draws_content.
-  bool hit_testable_without_draws_content_ : 1;
+  // Tracks if this layer should participate in hit testing.
+  bool hit_testable_ : 1;
   bool is_resized_by_browser_controls_ : 1;
 
   // TODO(bokan): This can likely be removed after blink-gen-property-trees
diff --git a/cc/layers/layer_unittest.cc b/cc/layers/layer_unittest.cc
index f207739..9a6e3342 100644
--- a/cc/layers/layer_unittest.cc
+++ b/cc/layers/layer_unittest.cc
@@ -1420,30 +1420,29 @@
       LayerImpl::Create(host_impl_.active_tree(), 1);
   EXPECT_SET_NEEDS_FULL_TREE_SYNC(1,
                                   layer_tree_host_->SetRootLayer(root_layer));
-  EXPECT_CALL(*layer_tree_host_, SetNeedsCommit()).Times(3);
+  EXPECT_CALL(*layer_tree_host_, SetNeedsCommit()).Times(5);
 
   // A layer that draws content should be hit testable.
   root_layer->SetIsDrawable(true);
+  root_layer->SetHitTestable(true);
   root_layer->PushPropertiesTo(impl_layer.get());
   EXPECT_TRUE(impl_layer->DrawsContent());
-  EXPECT_FALSE(impl_layer->hit_testable_without_draws_content());
-  EXPECT_TRUE(impl_layer->ShouldHitTest());
+  EXPECT_TRUE(impl_layer->HitTestable());
 
   // A layer that does not draw content and does not hit test without drawing
   // content should not be hit testable.
   root_layer->SetIsDrawable(false);
+  root_layer->SetHitTestable(false);
   root_layer->PushPropertiesTo(impl_layer.get());
   EXPECT_FALSE(impl_layer->DrawsContent());
-  EXPECT_FALSE(impl_layer->hit_testable_without_draws_content());
-  EXPECT_FALSE(impl_layer->ShouldHitTest());
+  EXPECT_FALSE(impl_layer->HitTestable());
 
   // |SetHitTestableWithoutDrawsContent| should cause a layer to become hit
   // testable even though it does not draw content.
-  root_layer->SetHitTestableWithoutDrawsContent(true);
+  root_layer->SetHitTestable(true);
   root_layer->PushPropertiesTo(impl_layer.get());
   EXPECT_FALSE(impl_layer->DrawsContent());
-  EXPECT_TRUE(impl_layer->hit_testable_without_draws_content());
-  EXPECT_TRUE(impl_layer->ShouldHitTest());
+  EXPECT_TRUE(impl_layer->HitTestable());
 }
 
 void ReceiveCopyOutputResult(int* result_count,
diff --git a/cc/test/layer_tree_json_parser.cc b/cc/test/layer_tree_json_parser.cc
index 8d600d5..a7819bc 100644
--- a/cc/test/layer_tree_json_parser.cc
+++ b/cc/test/layer_tree_json_parser.cc
@@ -35,6 +35,14 @@
   bool draws_content;
   success &= dict->GetBoolean("DrawsContent", &draws_content);
 
+  bool hit_testable;
+  // If we cannot load hit_testable, we may try loading the old version, since
+  // we do not record |hit_testable_without_draws_content| in the past, we use
+  // |draws_content| as the value of |hit_testable|.
+  if (!dict->GetBoolean("HitTestable", &hit_testable)) {
+    hit_testable = draws_content;
+  }
+
   scoped_refptr<Layer> new_layer;
   if (layer_type == "SolidColorLayer") {
     new_layer = SolidColorLayer::Create();
@@ -84,6 +92,7 @@
   }
   new_layer->SetBounds(gfx::Size(width, height));
   new_layer->SetIsDrawable(draws_content);
+  new_layer->SetHitTestable(hit_testable);
 
   double opacity;
   if (dict->GetDouble("Opacity", &opacity))
diff --git a/cc/test/layer_tree_pixel_test.cc b/cc/test/layer_tree_pixel_test.cc
index 8526bd0..08818c80 100644
--- a/cc/test/layer_tree_pixel_test.cc
+++ b/cc/test/layer_tree_pixel_test.cc
@@ -159,6 +159,7 @@
     const gfx::Rect& rect, SkColor color) {
   scoped_refptr<SolidColorLayer> layer = SolidColorLayer::Create();
   layer->SetIsDrawable(true);
+  layer->SetHitTestable(true);
   layer->SetBounds(rect.size());
   layer->SetPosition(gfx::PointF(rect.origin()));
   layer->SetOffsetToTransformParent(
diff --git a/cc/test/layer_tree_test.cc b/cc/test/layer_tree_test.cc
index d2a248a..a977f6b 100644
--- a/cc/test/layer_tree_test.cc
+++ b/cc/test/layer_tree_test.cc
@@ -158,9 +158,11 @@
 
   inner_viewport_container_layer->SetBounds(inner_bounds);
   inner_viewport_scroll_layer->SetScrollable(inner_bounds);
+  inner_viewport_scroll_layer->SetHitTestable(true);
   inner_viewport_scroll_layer->SetBounds(outer_bounds);
   outer_viewport_container_layer->SetBounds(outer_bounds);
   outer_scroll_layer->SetScrollable(outer_bounds);
+  outer_scroll_layer->SetHitTestable(true);
 
   inner_viewport_scroll_layer->SetIsContainerForFixedPositionLayers(true);
   outer_scroll_layer->SetIsContainerForFixedPositionLayers(true);
@@ -184,6 +186,7 @@
 
   outer_viewport_scroll_layer->SetBounds(scroll_bounds);
   outer_viewport_scroll_layer->SetIsDrawable(true);
+  outer_viewport_scroll_layer->SetHitTestable(true);
   CreateVirtualViewportLayers(root_layer, outer_viewport_scroll_layer,
                               inner_bounds, outer_bounds, host);
 }
@@ -906,6 +909,7 @@
                                              initial_device_scale_factor_,
                                              viz::LocalSurfaceIdAllocation());
   layer_tree_host()->root_layer()->SetIsDrawable(true);
+  layer_tree_host()->root_layer()->SetHitTestable(true);
   layer_tree_host()->SetElementIdsForTesting();
 }
 
diff --git a/cc/trees/effect_node.h b/cc/trees/effect_node.h
index 7302bcee..f9ad9713 100644
--- a/cc/trees/effect_node.h
+++ b/cc/trees/effect_node.h
@@ -79,9 +79,6 @@
   bool has_masking_child : 1;
   // Whether this node has a mask. This bit is not used when using layer lists.
   bool is_masked : 1;
-  // Whether layers associated with this node have a mask or ancestor mask that
-  // could affect the layer's hit testable bit.
-  bool hit_test_may_be_affected_by_mask : 1;
   // Whether this node's effect has been changed since the last
   // frame. Needed in order to compute damage rect.
   bool effect_changed : 1;
diff --git a/cc/trees/layer_tree_host.cc b/cc/trees/layer_tree_host.cc
index 687ab1b..59c4845 100644
--- a/cc/trees/layer_tree_host.cc
+++ b/cc/trees/layer_tree_host.cc
@@ -883,9 +883,11 @@
   if (inner_viewport_scroll_delta.IsZero() && info.page_scale_delta == 1.f &&
       info.elastic_overscroll_delta.IsZero() && !info.top_controls_delta &&
       !info.browser_controls_constraint_changed &&
-      !info.scroll_gesture_did_end) {
+      !info.scroll_gesture_did_end &&
+      info.is_pinch_gesture_active == is_pinch_gesture_active_from_impl_) {
     return;
   }
+  is_pinch_gesture_active_from_impl_ = info.is_pinch_gesture_active;
 
   // Preemptively apply the scroll offset and scale delta here before sending
   // it to the client.  If the client comes back and sets it to the same
@@ -1326,11 +1328,16 @@
       this, [](Layer* layer) { layer->SetNeedsDisplay(); });
 }
 
-void LayerTreeHost::SetExternalPageScaleFactor(float page_scale_factor) {
-  if (external_page_scale_factor_ == page_scale_factor)
+void LayerTreeHost::SetExternalPageScaleFactor(
+    float page_scale_factor,
+    bool is_external_pinch_gesture_active) {
+  if (external_page_scale_factor_ == page_scale_factor &&
+      is_external_pinch_gesture_active_ == is_external_pinch_gesture_active) {
     return;
+  }
 
   external_page_scale_factor_ = page_scale_factor;
+  is_external_pinch_gesture_active_ = is_external_pinch_gesture_active;
   SetNeedsCommit();
 }
 
@@ -1651,6 +1658,7 @@
   host_impl->SetHasGpuRasterizationTrigger(has_gpu_rasterization_trigger_);
   host_impl->SetContentHasSlowPaths(content_has_slow_paths_);
   host_impl->SetContentHasNonAAPaint(content_has_non_aa_paint_);
+  host_impl->set_pinch_gesture_active(is_external_pinch_gesture_active_);
   RecordGpuRasterizationHistogram(host_impl);
 
   host_impl->SetDebugState(debug_state_);
diff --git a/cc/trees/layer_tree_host.h b/cc/trees/layer_tree_host.h
index 85c520d..cd2c6e69 100644
--- a/cc/trees/layer_tree_host.h
+++ b/cc/trees/layer_tree_host.h
@@ -471,7 +471,11 @@
   // 'external_page_scale_factor', a value that affects raster scale in the
   // same way that page_scale_factor does, but doesn't affect any geometry
   // calculations.
-  void SetExternalPageScaleFactor(float page_scale_factor);
+  void SetExternalPageScaleFactor(float page_scale_factor,
+                                  bool is_external_pinch_gesture_active);
+  bool is_external_pinch_gesture_active_for_testing() {
+    return is_external_pinch_gesture_active_;
+  }
 
   // Requests that we force send RenderFrameMetadata with the next frame.
   void RequestForceSendMetadata() { force_send_metadata_request_ = true; }
@@ -805,6 +809,9 @@
   float min_page_scale_factor_ = 1.f;
   float max_page_scale_factor_ = 1.f;
   float external_page_scale_factor_ = 1.f;
+  bool is_external_pinch_gesture_active_ = false;
+  // Used to track the out-bound state for ApplyViewportChanges.
+  bool is_pinch_gesture_active_from_impl_ = false;
 
   int raster_color_space_id_ = -1;
   gfx::ColorSpace raster_color_space_;
diff --git a/cc/trees/layer_tree_host_impl.cc b/cc/trees/layer_tree_host_impl.cc
index 72dfe949..62add2c4 100644
--- a/cc/trees/layer_tree_host_impl.cc
+++ b/cc/trees/layer_tree_host_impl.cc
@@ -2631,7 +2631,13 @@
   // a result we do async hit test on any surface layers that
   bool assume_overlap = false;
   for (const auto* layer : base::Reversed(*active_tree())) {
-    if (!layer->ShouldHitTest())
+    // Viz hit test needs to collect information for pointer-events: none OOPIFs
+    // as well. Now Layer::HitTestable ignores pointer-events property, but this
+    // early out will not work correctly if we integrate has_pointer_events_none
+    // into Layer::HitTestable, so we make sure we don't skip surface layers
+    // that draws content but has pointer-events: none property.
+    if (!(layer->HitTestable() ||
+          (layer->is_surface_layer() && layer->DrawsContent())))
       continue;
 
     if (layer->is_surface_layer()) {
@@ -2639,7 +2645,8 @@
       // If a surface layer is created not by child frame compositor or the
       // frame owner has pointer-events: none property, the surface layer
       // becomes not hit testable. We should not generate data for it.
-      if (!surface_layer->surface_hit_testable()) {
+      if (!surface_layer->surface_hit_testable() ||
+          !surface_layer->range().IsValid()) {
         // We collect any overlapped regions that does not have pointer-events:
         // none.
         if (!surface_layer->has_pointer_events_none() && !assume_overlap) {
diff --git a/cc/trees/layer_tree_host_impl.h b/cc/trees/layer_tree_host_impl.h
index 8e76053..c2d3791e 100644
--- a/cc/trees/layer_tree_host_impl.h
+++ b/cc/trees/layer_tree_host_impl.h
@@ -591,6 +591,15 @@
   }
 
   bool pinch_gesture_active() const { return pinch_gesture_active_; }
+  // Used to set the pinch gesture active state when the pinch gesture is
+  // handled on another layer tree. In a page with OOPIFs, only the main
+  // frame's layer tree directly handles pinch events. But layer trees for
+  // sub-frames need to know when pinch gestures are active so they can
+  // throttle the re-rastering. This function allows setting this flag on
+  // OOPIF layer trees using information sent (initially) from the main-frame.
+  void set_pinch_gesture_active(bool external_pinch_gesture_active) {
+    pinch_gesture_active_ = external_pinch_gesture_active;
+  }
 
   void SetTreePriority(TreePriority priority);
   TreePriority GetTreePriority() const;
@@ -1006,6 +1015,15 @@
   bool did_scroll_x_for_scroll_gesture_;
   bool did_scroll_y_for_scroll_gesture_;
 
+  // This value is used to allow the compositor to throttle re-rastering during
+  // pinch gestures, when the page scale factor may be changing frequently. It
+  // is set in one of two ways:
+  // i) In a layer tree serving the root of the frame/compositor tree, it is
+  // directly set during processing of GesturePinch events on the impl thread
+  // (only the root layer tree has access to these).
+  // ii) In a layer tree serving a sub-frame in the frame/compositor tree, it
+  // is set from the main thread during the commit process, using information
+  // sent from the root layer tree via IPC messaging.
   bool pinch_gesture_active_ = false;
   bool pinch_gesture_end_should_clear_scrolling_node_ = false;
 
diff --git a/cc/trees/layer_tree_host_impl_unittest.cc b/cc/trees/layer_tree_host_impl_unittest.cc
index 26e4d88..795737f 100644
--- a/cc/trees/layer_tree_host_impl_unittest.cc
+++ b/cc/trees/layer_tree_host_impl_unittest.cc
@@ -394,6 +394,7 @@
         LayerImpl::Create(layer_tree_impl, kPageScaleLayerId);
 
     inner_scroll->SetScrollable(viewport_scroll_bounds);
+    inner_scroll->SetHitTestable(true);
     inner_scroll->SetElementId(
         LayerIdToElementIdForTesting(inner_scroll->id()));
     inner_scroll->SetBounds(content_size);
@@ -408,6 +409,7 @@
     std::unique_ptr<LayerImpl> outer_scroll =
         LayerImpl::Create(layer_tree_impl, kOuterViewportScrollLayerId);
     outer_scroll->SetScrollable(content_size);
+    outer_scroll->SetHitTestable(true);
     outer_scroll->SetElementId(
         LayerIdToElementIdForTesting(outer_scroll->id()));
     outer_scroll->layer_tree_impl()
@@ -465,6 +467,7 @@
     std::unique_ptr<LayerImpl> scroll = LayerImpl::Create(layer_tree_impl, 3);
     scroll->SetBounds(scroll_content_size);
     scroll->SetScrollable(content_size);
+    scroll->SetHitTestable(true);
     scroll->SetElementId(LayerIdToElementIdForTesting(scroll->id()));
     scroll->SetDrawsContent(true);
 
@@ -484,15 +487,17 @@
       squash1->test_properties()->opacity = 0.0f;
       // The transparent layer should still participate in hit testing even
       // through it does not draw content.
-      squash1->SetHitTestableWithoutDrawsContent(true);
+      squash1->SetHitTestable(true);
     } else {
       squash1->SetDrawsContent(true);
+      squash1->SetHitTestable(true);
     }
 
     std::unique_ptr<LayerImpl> squash2 = LayerImpl::Create(layer_tree_impl, 6);
     squash2->SetBounds(gfx::Size(140, 300));
     squash2->test_properties()->position = gfx::PointF(220, 300);
     squash2->SetDrawsContent(true);
+    squash2->SetHitTestable(true);
 
     scroll->test_properties()->AddChild(std::move(squash2));
     root->test_properties()->AddChild(std::move(scroll));
@@ -540,6 +545,7 @@
     content_layer->SetBounds(content_size);
     host_impl_->OuterViewportScrollLayer()->SetBounds(content_size);
     host_impl_->OuterViewportScrollLayer()->SetScrollable(viewport_size);
+    host_impl_->OuterViewportScrollLayer()->SetHitTestable(true);
 
     LayerImpl* outer_clip =
         host_impl_->OuterViewportScrollLayer()->test_properties()->parent;
@@ -552,6 +558,7 @@
     inner_clip_layer->SetBounds(viewport_size);
     host_impl_->InnerViewportScrollLayer()->SetBounds(viewport_size);
     host_impl_->InnerViewportScrollLayer()->SetScrollable(viewport_size);
+    host_impl_->InnerViewportScrollLayer()->SetHitTestable(true);
 
     host_impl_->active_tree()->BuildPropertyTreesForTesting();
 
@@ -571,6 +578,7 @@
     gfx::Size scroll_container_bounds =
         gfx::Size(size.width() / 2, size.height() / 2);
     layer->SetScrollable(scroll_container_bounds);
+    layer->SetHitTestable(true);
     return layer;
   }
 
@@ -710,6 +718,7 @@
     LayerImpl* overflow = scroll_layer->test_properties()->children[0];
     overflow->SetBounds(overflow_size);
     overflow->SetScrollable(gfx::Size(100, 100));
+    overflow->SetHitTestable(true);
     overflow->SetElementId(LayerIdToElementIdForTesting(overflow->id()));
     overflow->layer_tree_impl()
         ->property_trees()
@@ -1035,6 +1044,7 @@
 
   root->SetBounds(gfx::Size(110, 110));
   root->SetScrollable(gfx::Size(10, 10));
+  root->SetHitTestable(true);
   root->SetElementId(LayerIdToElementIdForTesting(root->id()));
   root->layer_tree_impl()
       ->property_trees()
@@ -1334,8 +1344,10 @@
   std::unique_ptr<LayerImpl> scroll = LayerImpl::Create(layer_tree_impl, 3);
   scroll->SetBounds(scroll_content_size);
   scroll->SetScrollable(content_size);
+  scroll->SetHitTestable(true);
   scroll->SetElementId(LayerIdToElementIdForTesting(scroll->id()));
   scroll->SetDrawsContent(true);
+  scroll->SetHitTestable(true);
 
   std::unique_ptr<SolidColorScrollbarLayerImpl> drawn_scrollbar =
       SolidColorScrollbarLayerImpl::Create(layer_tree_impl, 4, VERTICAL, 10, 0,
@@ -1344,12 +1356,14 @@
   drawn_scrollbar->test_properties()->position = gfx::PointF(345, 0);
   drawn_scrollbar->SetScrollElementId(scroll->element_id());
   drawn_scrollbar->SetDrawsContent(true);
+  drawn_scrollbar->SetHitTestable(true);
   drawn_scrollbar->test_properties()->opacity = 1.f;
 
   std::unique_ptr<LayerImpl> squash = LayerImpl::Create(layer_tree_impl, 5);
   squash->SetBounds(gfx::Size(140, 300));
   squash->test_properties()->position = gfx::PointF(220, 0);
   squash->SetDrawsContent(true);
+  squash->SetHitTestable(true);
 
   scroll->test_properties()->AddChild(std::move(drawn_scrollbar));
   scroll->test_properties()->AddChild(std::move(squash));
@@ -1775,6 +1789,7 @@
   LayerImpl* overflow = scroll_layer->test_properties()->children[0];
   overflow->SetBounds(overflow_size);
   overflow->SetScrollable(gfx::Size(100, 100));
+  overflow->SetHitTestable(true);
   overflow->SetElementId(LayerIdToElementIdForTesting(overflow->id()));
   overflow->layer_tree_impl()
       ->property_trees()
@@ -1944,6 +1959,7 @@
   LayerImpl* overflow = scroll_layer->test_properties()->children[0];
   overflow->SetBounds(overflow_size);
   overflow->SetScrollable(gfx::Size(100, 100));
+  overflow->SetHitTestable(true);
   overflow->SetElementId(LayerIdToElementIdForTesting(overflow->id()));
   overflow->layer_tree_impl()
       ->property_trees()
@@ -2386,6 +2402,7 @@
     // frame (outer viewport) such that it matches the width of the content,
     // preventing horizontal scrolling. Replicate that behavior here.
     host_impl_->OuterViewportScrollLayer()->SetScrollable(outer_viewport_size);
+    host_impl_->OuterViewportScrollLayer()->SetHitTestable(true);
     LayerImpl* outer_clip =
         host_impl_->OuterViewportScrollLayer()->test_properties()->parent;
     outer_clip->SetBounds(outer_viewport_size);
@@ -3990,6 +4007,7 @@
     scrollbar_1_ = scrollbar_1.get();
     scrollbar_1->SetScrollElementId(root_scroll->element_id());
     scrollbar_1->SetDrawsContent(true);
+    scrollbar_1->SetHitTestable(true);
     scrollbar_1->SetBounds(scrollbar_size_1);
     TouchActionRegion touch_action_region;
     touch_action_region.Union(kTouchActionNone, gfx::Rect(scrollbar_size_1));
@@ -4012,12 +4030,15 @@
     child->test_properties()->position = gfx::PointF(50, 50);
     child->SetBounds(child_layer_size);
     child->SetDrawsContent(true);
+    child->SetHitTestable(true);
     child->SetScrollable(gfx::Size(100, 100));
+    child->SetHitTestable(true);
     child->SetElementId(LayerIdToElementIdForTesting(child->id()));
     ElementId child_element_id = child->element_id();
 
     scrollbar_2->SetScrollElementId(child_element_id);
     scrollbar_2->SetDrawsContent(true);
+    scrollbar_2->SetHitTestable(true);
     scrollbar_2->SetBounds(scrollbar_size_2);
     scrollbar_2->SetCurrentPos(0);
     scrollbar_2->test_properties()->position = gfx::PointF(0, 0);
@@ -4338,6 +4359,7 @@
 
   // Check scrollbar registration on a sublayer.
   child->SetScrollable(viewport_size);
+  child->SetHitTestable(true);
   child->SetElementId(LayerIdToElementIdForTesting(child->id()));
   ElementId child_scroll_element_id = child->element_id();
   root_scroll->test_properties()->AddChild(std::move(child));
@@ -5920,6 +5942,7 @@
       LayerImpl::Create(host_impl_->active_tree(), id + 2);
 
   child->SetScrollable(sub_content_layer_size);
+  child->SetHitTestable(true);
   child->SetElementId(LayerIdToElementIdForTesting(child->id()));
   child->SetBounds(sub_content_size);
   child->test_properties()->position = gfx::PointF();
@@ -6474,6 +6497,7 @@
   std::unique_ptr<LayerImpl> scroll_layer =
       LayerImpl::Create(host_impl_->active_tree(), 12);
   scroll_layer->SetScrollable(surface_size);
+  scroll_layer->SetHitTestable(true);
   scroll_layer->SetElementId(LayerIdToElementIdForTesting(scroll_layer->id()));
   scroll_layer->SetBounds(contents_size);
   scroll_layer->test_properties()->position = gfx::PointF();
@@ -7382,6 +7406,7 @@
       gfx::Size(child->bounds().width(), child->bounds().height() / 2);
   clip_layer->SetBounds(scroll_container_bounds);
   child->SetScrollable(scroll_container_bounds);
+  child->SetHitTestable(true);
   // The rotation depends on the layer's transform origin, and the child layer
   // is a different size than the clip, so make sure the clip layer's origin
   // lines up over the child.
@@ -7615,6 +7640,7 @@
       scroll_layer->test_properties()->parent->test_properties()->parent;
   clip_layer->SetBounds(gfx::Size(10, 20));
   scroll_layer->SetScrollable(gfx::Size(10, 20));
+  scroll_layer->SetHitTestable(true);
   host_impl_->active_tree()->BuildPropertyTreesForTesting();
 
   host_impl_->BindToClient(&scroll_watcher);
@@ -7728,6 +7754,7 @@
       scroll_layer->test_properties()->parent->test_properties()->parent;
   clip_layer->SetBounds(gfx::Size(10, 20));
   scroll_layer->SetScrollable(gfx::Size(10, 20));
+  scroll_layer->SetHitTestable(true);
   scroll_layer->SetDrawsContent(true);
 
   // Draw first frame to clear any pending draws and check scroll.
@@ -8148,6 +8175,7 @@
 
   clip_layer->SetBounds(gfx::Size(50, 50));
   scroll_layer->SetScrollable(gfx::Size(50, 50));
+  scroll_layer->SetHitTestable(true);
   host_impl_->active_tree()->BuildPropertyTreesForTesting();
 
   host_impl_->active_tree()->SetDeviceViewportSize(gfx::Size(50, 50));
@@ -8256,6 +8284,7 @@
     std::unique_ptr<LayerImpl> scroll = LayerImpl::Create(layer_tree_impl, 11);
     scroll->SetBounds(gfx::Size(400, 400));
     scroll->SetScrollable(content_size);
+    scroll->SetHitTestable(true);
     scroll->SetElementId(LayerIdToElementIdForTesting(scroll->id()));
     scroll->SetDrawsContent(true);
 
@@ -8377,6 +8406,7 @@
     std::unique_ptr<LayerImpl> scroll = LayerImpl::Create(layer_tree_impl, 11);
     scroll->SetBounds(gfx::Size(400, 400));
     scroll->SetScrollable(viewport_size);
+    scroll->SetHitTestable(true);
     scroll->SetElementId(LayerIdToElementIdForTesting(scroll->id()));
     scroll->SetDrawsContent(true);
 
@@ -8465,12 +8495,14 @@
     std::unique_ptr<LayerImpl> scroll = LayerImpl::Create(layer_tree_impl, 11);
     scroll->SetBounds(gfx::Size(400, 400));
     scroll->SetScrollable(content_size);
+    scroll->SetHitTestable(true);
     scroll->SetElementId(LayerIdToElementIdForTesting(scroll->id()));
     scroll->SetDrawsContent(true);
 
     std::unique_ptr<LayerImpl> scroll2 = LayerImpl::Create(layer_tree_impl, 13);
     scroll2->SetBounds(gfx::Size(500, 500));
     scroll2->SetScrollable(gfx::Size(300, 300));
+    scroll2->SetHitTestable(true);
     scroll2->SetElementId(LayerIdToElementIdForTesting(scroll2->id()));
     scroll2->SetDrawsContent(true);
 
@@ -8588,6 +8620,7 @@
     std::unique_ptr<LayerImpl> scroll = LayerImpl::Create(layer_tree_impl, 11);
     scroll->SetBounds(gfx::Size(1200, 1200));
     scroll->SetScrollable(content_size);
+    scroll->SetHitTestable(true);
     scroll->SetElementId(LayerIdToElementIdForTesting(scroll->id()));
     scroll->SetDrawsContent(true);
 
@@ -8599,6 +8632,7 @@
     std::unique_ptr<LayerImpl> scroll2 = LayerImpl::Create(layer_tree_impl, 15);
     scroll2->SetBounds(gfx::Size(1200, 1200));
     scroll2->SetScrollable(gfx::Size(600, 600));
+    scroll2->SetHitTestable(true);
     scroll2->SetElementId(LayerIdToElementIdForTesting(scroll2->id()));
     scroll2->SetDrawsContent(true);
 
@@ -9842,6 +9876,7 @@
 
   gfx::ScrollOffset scroll_offset(100000, 0);
   scrolling_layer->SetScrollable(content_layer_bounds);
+  scrolling_layer->SetHitTestable(true);
   scrolling_layer->SetElementId(
       LayerIdToElementIdForTesting(scrolling_layer->id()));
   host_impl_->pending_tree()->BuildPropertyTreesForTesting();
@@ -10346,6 +10381,7 @@
   LayerImpl* scroll_layer =
       host_impl_->active_tree()->LayerById(scroll_layer_id);
   scroll_layer->SetDrawsContent(true);
+  scroll_layer->SetHitTestable(true);
 
   int page_scale_layer_id = 5;
   LayerImpl* page_scale_layer =
@@ -10355,6 +10391,7 @@
   std::unique_ptr<LayerImpl> occluder_layer =
       LayerImpl::Create(host_impl_->active_tree(), occluder_layer_id);
   occluder_layer->SetDrawsContent(true);
+  occluder_layer->SetHitTestable(true);
   occluder_layer->SetBounds(content_size);
   occluder_layer->test_properties()->position = gfx::PointF();
 
@@ -10382,11 +10419,13 @@
   LayerImpl* scroll_layer =
       host_impl_->active_tree()->LayerById(scroll_layer_id);
   scroll_layer->SetDrawsContent(true);
+  scroll_layer->SetHitTestable(true);
 
   int occluder_layer_id = 6;
   std::unique_ptr<LayerImpl> occluder_layer =
       LayerImpl::Create(host_impl_->active_tree(), occluder_layer_id);
   occluder_layer->SetDrawsContent(true);
+  occluder_layer->SetHitTestable(true);
   occluder_layer->SetBounds(content_size);
   occluder_layer->test_properties()->position = gfx::PointF(-10.f, -10.f);
 
@@ -11255,6 +11294,7 @@
     std::unique_ptr<LayerImpl> scroll = LayerImpl::Create(layer_tree_impl, 11);
     scroll->SetBounds(scroll_content_size);
     scroll->SetScrollable(root_layer_size);
+    scroll->SetHitTestable(true);
     scroll->SetElementId(LayerIdToElementIdForTesting(scroll->id()));
     scroll->SetDrawsContent(true);
 
@@ -11323,6 +11363,7 @@
         LayerImpl::Create(layer_tree_impl, kPageScaleLayerId);
 
     inner_scroll->SetScrollable(inner_viewport);
+    inner_scroll->SetHitTestable(true);
     inner_scroll->SetElementId(
         LayerIdToElementIdForTesting(inner_scroll->id()));
     inner_scroll->SetBounds(outer_viewport);
@@ -11337,6 +11378,7 @@
     std::unique_ptr<LayerImpl> outer_scroll =
         LayerImpl::Create(layer_tree_impl, kOuterViewportScrollLayerId);
     outer_scroll->SetScrollable(outer_viewport);
+    outer_scroll->SetHitTestable(true);
     outer_scroll->SetElementId(
         LayerIdToElementIdForTesting(outer_scroll->id()));
     outer_scroll->layer_tree_impl()
@@ -13567,6 +13609,7 @@
   child->SetBounds(child_layer_size);
   child->SetDrawsContent(true);
   child->SetScrollable(gfx::Size(100, 100));
+  child->SetHitTestable(true);
   child->SetElementId(LayerIdToElementIdForTesting(child->id()));
   ElementId child_element_id = child->element_id();
 
@@ -14221,16 +14264,19 @@
   rotate.Rotate(45);
   surface_child1->test_properties()->transform = rotate;
   surface_child1->SetDrawsContent(true);
+  surface_child1->SetHitTestable(true);
   surface_child1->SetSurfaceHitTestable(true);
 
   surface_child2->test_properties()->position = gfx::PointF(450, 300);
   surface_child2->SetBounds(gfx::Size(100, 100));
   surface_child2->SetDrawsContent(true);
+  surface_child2->SetHitTestable(true);
   surface_child2->SetSurfaceHitTestable(true);
 
   overlapping_layer->test_properties()->position = gfx::PointF(500, 350);
   overlapping_layer->SetBounds(gfx::Size(200, 200));
   overlapping_layer->SetDrawsContent(true);
+  overlapping_layer->SetHitTestable(true);
 
   viz::LocalSurfaceId child_local_surface_id(2,
                                              base::UnguessableToken::Create());
@@ -14327,12 +14373,14 @@
   surface_child1->test_properties()->position = gfx::PointF(0, 0);
   surface_child1->SetBounds(gfx::Size(100, 100));
   surface_child1->SetDrawsContent(true);
+  surface_child1->SetHitTestable(true);
   surface_child1->SetSurfaceHitTestable(true);
   surface_child1->SetHasPointerEventsNone(false);
 
   surface_child2->test_properties()->position = gfx::PointF(50, 50);
   surface_child2->SetBounds(gfx::Size(100, 100));
   surface_child2->SetDrawsContent(true);
+  surface_child2->SetHitTestable(true);
   surface_child2->SetSurfaceHitTestable(false);
   surface_child2->SetHasPointerEventsNone(true);
 
@@ -14405,6 +14453,7 @@
   surface_child->test_properties()->position = gfx::PointF(0, 0);
   surface_child->SetBounds(gfx::Size(100, 100));
   surface_child->SetDrawsContent(true);
+  surface_child->SetHitTestable(true);
   surface_child->SetSurfaceHitTestable(true);
   surface_child->SetHasPointerEventsNone(false);
 
@@ -14430,6 +14479,7 @@
     layer->test_properties()->position = gfx::PointF(110, 110);
     layer->SetBounds(gfx::Size(1, 1));
     layer->SetDrawsContent(true);
+    layer->SetHitTestable(true);
     host_impl_->active_tree()
         ->root_layer_for_testing()
         ->test_properties()
@@ -14469,6 +14519,93 @@
             hit_test_region_list->regions[0].rect.ToString());
 }
 
+TEST_F(HitTestRegionListGeneratingLayerTreeHostImplTest, InvalidFrameSinkId) {
+  // Setup surface layers in LayerTreeHostImpl.
+  host_impl_->CreatePendingTree();
+  host_impl_->ActivateSyncTree();
+
+  // The structure of the layer tree:
+  // +-Root (1024x768)
+  // +---surface_child1 (0, 0), 100x100
+  // +---surface_child2 (0, 0), 50x50, frame_sink_id = (0, 0)
+  std::unique_ptr<SurfaceLayerImpl> surface_child1 =
+      SurfaceLayerImpl::Create(host_impl_->active_tree(), 2);
+
+  host_impl_->active_tree()->SetDeviceViewportSize(gfx::Size(1024, 768));
+
+  surface_child1->test_properties()->position = gfx::PointF(0, 0);
+  surface_child1->SetBounds(gfx::Size(100, 100));
+  surface_child1->SetDrawsContent(true);
+  surface_child1->SetHitTestable(true);
+  surface_child1->SetSurfaceHitTestable(true);
+  surface_child1->SetHasPointerEventsNone(false);
+
+  viz::LocalSurfaceId child_local_surface_id(2,
+                                             base::UnguessableToken::Create());
+  viz::FrameSinkId frame_sink_id(2, 0);
+  viz::SurfaceId child_surface_id(frame_sink_id, child_local_surface_id);
+  surface_child1->SetRange(viz::SurfaceRange(base::nullopt, child_surface_id),
+                           base::nullopt);
+
+  std::unique_ptr<SurfaceLayerImpl> surface_child2 =
+      SurfaceLayerImpl::Create(host_impl_->active_tree(), 3);
+
+  surface_child2->test_properties()->position = gfx::PointF(0, 0);
+  surface_child2->SetBounds(gfx::Size(50, 50));
+  surface_child2->SetDrawsContent(true);
+  surface_child2->SetHitTestable(true);
+  surface_child2->SetSurfaceHitTestable(true);
+  surface_child2->SetHasPointerEventsNone(false);
+
+  surface_child2->SetRange(viz::SurfaceRange(base::nullopt, viz::SurfaceId()),
+                           base::nullopt);
+
+  std::unique_ptr<LayerImpl> root =
+      LayerImpl::Create(host_impl_->active_tree(), 1);
+  host_impl_->active_tree()->SetRootLayerForTesting(std::move(root));
+  host_impl_->active_tree()
+      ->root_layer_for_testing()
+      ->test_properties()
+      ->AddChild(std::move(surface_child1));
+
+  host_impl_->active_tree()
+      ->root_layer_for_testing()
+      ->test_properties()
+      ->AddChild(std::move(surface_child2));
+
+  constexpr gfx::Rect kFrameRect(0, 0, 1024, 768);
+
+  host_impl_->active_tree()->BuildPropertyTreesForTesting();
+  host_impl_->active_tree()->UpdateDrawProperties();
+  base::Optional<viz::HitTestRegionList> hit_test_region_list =
+      host_impl_->BuildHitTestData();
+  // Generating HitTestRegionList should have been enabled for this test.
+  ASSERT_TRUE(hit_test_region_list);
+
+  uint32_t expected_flags = viz::HitTestRegionFlags::kHitTestMouse |
+                            viz::HitTestRegionFlags::kHitTestTouch |
+                            viz::HitTestRegionFlags::kHitTestMine;
+  EXPECT_EQ(expected_flags, hit_test_region_list->flags);
+  EXPECT_EQ(kFrameRect, hit_test_region_list->bounds);
+  EXPECT_EQ(1u, hit_test_region_list->regions.size());
+
+  EXPECT_EQ(child_surface_id.frame_sink_id(),
+            hit_test_region_list->regions[0].frame_sink_id);
+  // We do not populate hit test region for a surface layer with invalid frame
+  // sink id to avoid deserialization failure. Instead we make the overlapping
+  // hit test region kHitTestAsk.
+  expected_flags = viz::HitTestRegionFlags::kHitTestMouse |
+                   viz::HitTestRegionFlags::kHitTestTouch |
+                   viz::HitTestRegionFlags::kHitTestChildSurface |
+                   viz::HitTestRegionFlags::kHitTestAsk;
+  EXPECT_EQ(expected_flags, hit_test_region_list->regions[0].flags);
+  gfx::Transform child1_transform;
+  EXPECT_TRUE(child1_transform.ApproximatelyEqual(
+      hit_test_region_list->regions[0].transform));
+  EXPECT_EQ(gfx::Rect(0, 0, 100, 100).ToString(),
+            hit_test_region_list->regions[0].rect.ToString());
+}
+
 TEST_F(LayerTreeHostImplTest, ImplThreadPhaseUponImplSideInvalidation) {
   LayerTreeSettings settings = DefaultSettings();
   CreateHostImpl(settings, CreateLayerTreeFrameSink());
diff --git a/cc/trees/layer_tree_host_unittest_scroll.cc b/cc/trees/layer_tree_host_unittest_scroll.cc
index 6a6f99b..667f67d 100644
--- a/cc/trees/layer_tree_host_unittest_scroll.cc
+++ b/cc/trees/layer_tree_host_unittest_scroll.cc
@@ -599,6 +599,7 @@
 
     child_layer_->SetIsDrawable(true);
     child_layer_->SetScrollable(root_layer->bounds());
+    child_layer_->SetHitTestable(true);
     child_layer_->SetElementId(
         LayerIdToElementIdForTesting(child_layer_->id()));
     child_layer_->SetBounds(root_scroll_layer_->bounds());
@@ -1130,6 +1131,7 @@
     switch (layer_tree_host()->SourceFrameNumber()) {
       case 0:
         scroll_layer->SetScrollable(root->bounds());
+        scroll_layer->SetHitTestable(true);
         // Set max_scroll_offset = (100, 100).
         scroll_layer->SetBounds(gfx::Size(root->bounds().width() + 100,
                                           root->bounds().height() + 100));
@@ -1410,6 +1412,7 @@
     scroll_layer->SetPosition(gfx::PointF());
     scroll_layer->SetIsDrawable(true);
     scroll_layer->SetScrollable(parent->bounds());
+    scroll_layer->SetHitTestable(true);
     scroll_layer->SetElementId(
         LayerIdToElementIdForTesting(scroll_layer->id()));
     scroll_layer->SetBounds(gfx::Size(parent->bounds().width() + 100,
diff --git a/cc/trees/layer_tree_impl.cc b/cc/trees/layer_tree_impl.cc
index 76d41d91..8202fa0 100644
--- a/cc/trees/layer_tree_impl.cc
+++ b/cc/trees/layer_tree_impl.cc
@@ -2133,10 +2133,7 @@
 }
 
 struct HitTestVisibleScrollableOrTouchableFunctor {
-  bool operator()(LayerImpl* layer) const {
-    return layer->scrollable() || layer->ShouldHitTest() ||
-           !layer->touch_action_region().region().IsEmpty();
-  }
+  bool operator()(LayerImpl* layer) const { return layer->HitTestable(); }
 };
 
 LayerImpl* LayerTreeImpl::FindLayerThatIsHitByPoint(
diff --git a/cc/trees/layer_tree_impl_unittest.cc b/cc/trees/layer_tree_impl_unittest.cc
index c752ebca..e703093 100644
--- a/cc/trees/layer_tree_impl_unittest.cc
+++ b/cc/trees/layer_tree_impl_unittest.cc
@@ -77,6 +77,7 @@
       root->test_properties()->sorting_context_id = root_sorting_context;
       root->SetBounds(bounds);
       root->SetDrawsContent(true);
+      root->SetHitTestable(true);
     }
     {
       gfx::Transform translate_z;
@@ -86,6 +87,7 @@
           left_child_sorting_context;
       left_child->SetBounds(bounds);
       left_child->SetDrawsContent(true);
+      left_child->SetHitTestable(true);
       left_child->test_properties()->should_flatten_transform = false;
     }
     {
@@ -96,6 +98,7 @@
           right_child_sorting_context;
       right_child->SetBounds(bounds);
       right_child->SetDrawsContent(true);
+      right_child->SetHitTestable(true);
     }
 
     root->test_properties()->AddChild(std::move(left_child));
@@ -124,6 +127,7 @@
   LayerImpl* root = root_layer();
   root->SetBounds(bounds);
   root->SetDrawsContent(true);
+  root->SetHitTestable(true);
 
   host_impl().active_tree()->SetDeviceViewportSize(root->bounds());
   host_impl().UpdateNumChildrenAndDrawPropertiesForActiveTree();
@@ -163,6 +167,7 @@
   LayerImpl* root = root_layer();
   root->SetBounds(bounds);
   root->SetDrawsContent(true);
+  root->SetHitTestable(true);
 
   host_impl().active_tree()->SetDeviceViewportSize(root->bounds());
   host_impl().UpdateNumChildrenAndDrawPropertiesForActiveTree();
@@ -185,12 +190,14 @@
   LayerImpl* root = root_layer();
   root->SetBounds(gfx::Size(100, 100));
   root->SetDrawsContent(true);
+  root->SetHitTestable(true);
 
   // Create hud and add it as a child of root.
   std::unique_ptr<HeadsUpDisplayLayerImpl> hud =
       HeadsUpDisplayLayerImpl::Create(host_impl().active_tree(), 11111);
   hud->SetBounds(gfx::Size(200, 200));
   hud->SetDrawsContent(true);
+  hud->SetHitTestable(true);
 
   host_impl().active_tree()->SetDeviceViewportSize(hud->bounds());
   host_impl().active_tree()->set_hud_layer(hud.get());
@@ -239,6 +246,7 @@
   root->test_properties()->transform = uninvertible_transform;
   root->SetBounds(gfx::Size(100, 100));
   root->SetDrawsContent(true);
+  root->SetHitTestable(true);
 
   host_impl().active_tree()->SetDeviceViewportSize(root->bounds());
   host_impl().UpdateNumChildrenAndDrawPropertiesForActiveTree();
@@ -295,6 +303,7 @@
     test_layer->test_properties()->position = gfx::PointF(50.f, 50.f);
     test_layer->SetBounds(gfx::Size(100, 100));
     test_layer->SetDrawsContent(true);
+    test_layer->SetHitTestable(true);
     root_layer()->test_properties()->AddChild(std::move(test_layer));
   }
 
@@ -343,6 +352,7 @@
   root->test_properties()->transform = rotation45_degrees_about_center;
   root->SetBounds(gfx::Size(100, 100));
   root->SetDrawsContent(true);
+  root->SetHitTestable(true);
 
   host_impl().active_tree()->SetDeviceViewportSize(root->bounds());
   host_impl().UpdateNumChildrenAndDrawPropertiesForActiveTree();
@@ -414,6 +424,7 @@
       LayerImpl::Create(host_impl().active_tree(), 5);
   test->SetBounds(gfx::Size(100, 100));
   test->SetDrawsContent(true);
+  test->SetHitTestable(true);
 
   clip->test_properties()->AddChild(std::move(test));
   scale->test_properties()->AddChild(std::move(clip));
@@ -445,12 +456,14 @@
   child1->SetBounds(gfx::Size(25, 25));
   child1->SetMasksToBounds(true);
   child1->SetDrawsContent(true);
+  child1->SetHitTestable(true);
 
   std::unique_ptr<LayerImpl> child2 =
       LayerImpl::Create(host_impl().active_tree(), 3);
   child2->SetBounds(gfx::Size(75, 75));
   child2->SetMasksToBounds(true);
   child2->SetDrawsContent(true);
+  child2->SetHitTestable(true);
 
   root->test_properties()->AddChild(std::move(child1));
   root->test_properties()->AddChild(std::move(child2));
@@ -480,6 +493,7 @@
       (perspective_projection_about_center * translation_by_z);
   root->SetBounds(gfx::Size(100, 100));
   root->SetDrawsContent(true);
+  root->SetHitTestable(true);
 
   host_impl().active_tree()->SetDeviceViewportSize(root->bounds());
   host_impl().UpdateNumChildrenAndDrawPropertiesForActiveTree();
@@ -535,6 +549,7 @@
     child->test_properties()->position = gfx::PointF(-50.f, -50.f);
     child->SetBounds(gfx::Size(300, 300));
     child->SetDrawsContent(true);
+    child->SetHitTestable(true);
     clipping_layer->test_properties()->AddChild(std::move(child));
     root->test_properties()->AddChild(std::move(clipping_layer));
   }
@@ -629,6 +644,7 @@
     rotated_leaf->SetBounds(gfx::Size(100, 100));
     rotated_leaf->test_properties()->transform = rotated_leaf_transform;
     rotated_leaf->SetDrawsContent(true);
+    rotated_leaf->SetHitTestable(true);
 
     grand_child->test_properties()->AddChild(std::move(rotated_leaf));
     child->test_properties()->AddChild(std::move(grand_child));
@@ -712,6 +728,7 @@
         gfx::PointF(60.f, 60.f);  // 70, 70 in screen spae
     child->SetBounds(gfx::Size(20, 20));
     child->SetDrawsContent(true);
+    child->SetHitTestable(true);
     intermediate_layer->test_properties()->AddChild(std::move(child));
     root->test_properties()->AddChild(std::move(intermediate_layer));
   }
@@ -754,6 +771,7 @@
   LayerImpl* root = root_layer();
   root->SetBounds(gfx::Size(100, 100));
   root->SetDrawsContent(true);
+  root->SetHitTestable(true);
   {
     // child 1 and child2 are initialized to overlap between x=50 and x=60.
     // grand_child is set to overlap both child1 and child2 between y=50 and
@@ -771,10 +789,12 @@
     child1->test_properties()->position = gfx::PointF(10.f, 10.f);
     child1->SetBounds(gfx::Size(50, 50));
     child1->SetDrawsContent(true);
+    child1->SetHitTestable(true);
 
     child2->test_properties()->position = gfx::PointF(50.f, 10.f);
     child2->SetBounds(gfx::Size(50, 50));
     child2->SetDrawsContent(true);
+    child2->SetHitTestable(true);
 
     // Remember that grand_child is positioned with respect to its parent (i.e.
     // child1).  In screen space, the intended position is (10, 50), with size
@@ -782,6 +802,7 @@
     grand_child1->test_properties()->position = gfx::PointF(0.f, 40.f);
     grand_child1->SetBounds(gfx::Size(100, 50));
     grand_child1->SetDrawsContent(true);
+    grand_child1->SetHitTestable(true);
 
     child1->test_properties()->AddChild(std::move(grand_child1));
     root->test_properties()->AddChild(std::move(child1));
@@ -898,6 +919,7 @@
   LayerImpl* root = root_layer();
   root->SetBounds(gfx::Size(100, 100));
   root->SetDrawsContent(true);
+  root->SetHitTestable(true);
   root->test_properties()->should_flatten_transform = false;
   root->test_properties()->sorting_context_id = 1;
   {
@@ -917,6 +939,7 @@
     child1->test_properties()->position = gfx::PointF(10.f, 10.f);
     child1->SetBounds(gfx::Size(50, 50));
     child1->SetDrawsContent(true);
+    child1->SetHitTestable(true);
     child1->test_properties()->should_flatten_transform = false;
     child1->test_properties()->sorting_context_id = 1;
 
@@ -926,6 +949,7 @@
     translate_z.Translate3d(0, 0, 10.f);
     child2->test_properties()->transform = translate_z;
     child2->SetDrawsContent(true);
+    child2->SetHitTestable(true);
     child2->test_properties()->should_flatten_transform = false;
     child2->test_properties()->sorting_context_id = 1;
 
@@ -935,6 +959,7 @@
     grand_child1->test_properties()->position = gfx::PointF(0.f, 40.f);
     grand_child1->SetBounds(gfx::Size(100, 50));
     grand_child1->SetDrawsContent(true);
+    grand_child1->SetHitTestable(true);
     grand_child1->test_properties()->should_flatten_transform = false;
 
     child1->test_properties()->AddChild(std::move(grand_child1));
@@ -1008,6 +1033,7 @@
   LayerImpl* root = root_layer();
   root->SetBounds(gfx::Size(100, 100));
   root->SetDrawsContent(true);
+  root->SetHitTestable(true);
   {
     std::unique_ptr<LayerImpl> child =
         LayerImpl::Create(host_impl().active_tree(), 2);
@@ -1017,11 +1043,13 @@
     child->test_properties()->position = gfx::PointF(10.f, 10.f);
     child->SetBounds(gfx::Size(1, 1));
     child->SetDrawsContent(true);
+    child->SetHitTestable(true);
     child->SetMasksToBounds(true);
 
     grand_child->test_properties()->position = gfx::PointF(0.f, 40.f);
     grand_child->SetBounds(gfx::Size(100, 50));
     grand_child->SetDrawsContent(true);
+    grand_child->SetHitTestable(true);
     grand_child->test_properties()->force_render_surface = true;
 
     // This should let |grand_child| "escape" |child|'s clip.
@@ -1049,6 +1077,7 @@
   LayerImpl* root = root_layer();
   root->SetBounds(gfx::Size(100, 100));
   root->SetDrawsContent(true);
+  root->SetHitTestable(true);
   {
     std::unique_ptr<LayerImpl> child =
         LayerImpl::Create(host_impl().active_tree(), 2);
@@ -1060,10 +1089,12 @@
     child->test_properties()->position = gfx::PointF(10.f, 10.f);
     child->SetBounds(gfx::Size(1, 1));
     child->SetDrawsContent(true);
+    child->SetHitTestable(true);
     child->SetMasksToBounds(true);
 
     scroll_child->SetBounds(gfx::Size(200, 200));
     scroll_child->SetDrawsContent(true);
+    scroll_child->SetHitTestable(true);
 
     // This should cause scroll child and its descendants to be affected by
     // |child|'s clip.
@@ -1071,6 +1102,7 @@
 
     grand_child->SetBounds(gfx::Size(200, 200));
     grand_child->SetDrawsContent(true);
+    grand_child->SetHitTestable(true);
     grand_child->test_properties()->force_render_surface = true;
 
     scroll_child->test_properties()->AddChild(std::move(grand_child));
@@ -1097,6 +1129,7 @@
   LayerImpl* root = root_layer();
   root->SetBounds(gfx::Size(100, 100));
   root->SetDrawsContent(true);
+  root->SetHitTestable(true);
   {
     // child 1 and child2 are initialized to overlap between x=50 and x=60.
     // grand_child is set to overlap both child1 and child2 between y=50 and
@@ -1114,11 +1147,13 @@
     child1->test_properties()->position = gfx::PointF(10.f, 10.f);
     child1->SetBounds(gfx::Size(50, 50));
     child1->SetDrawsContent(true);
+    child1->SetHitTestable(true);
     child1->test_properties()->force_render_surface = true;
 
     child2->test_properties()->position = gfx::PointF(50.f, 10.f);
     child2->SetBounds(gfx::Size(50, 50));
     child2->SetDrawsContent(true);
+    child2->SetHitTestable(true);
     child2->test_properties()->force_render_surface = true;
 
     // Remember that grand_child is positioned with respect to its parent (i.e.
@@ -1127,6 +1162,7 @@
     grand_child1->test_properties()->position = gfx::PointF(0.f, 40.f);
     grand_child1->SetBounds(gfx::Size(100, 50));
     grand_child1->SetDrawsContent(true);
+    grand_child1->SetHitTestable(true);
     grand_child1->test_properties()->force_render_surface = true;
 
     child1->test_properties()->AddChild(std::move(grand_child1));
@@ -1218,6 +1254,7 @@
   LayerImpl* root = root_layer();
   root->SetBounds(gfx::Size(100, 100));
   root->SetDrawsContent(true);
+  root->SetHitTestable(true);
 
   host_impl().active_tree()->SetDeviceViewportSize(root->bounds());
   host_impl().UpdateNumChildrenAndDrawPropertiesForActiveTree();
@@ -1295,6 +1332,7 @@
   root->test_properties()->transform = uninvertible_transform;
   root->SetBounds(gfx::Size(100, 100));
   root->SetDrawsContent(true);
+  root->SetHitTestable(true);
   root->SetTouchActionRegion(touch_action_region);
 
   host_impl().active_tree()->SetDeviceViewportSize(root->bounds());
@@ -1365,6 +1403,7 @@
     test_layer->test_properties()->position = gfx::PointF(50.f, 50.f);
     test_layer->SetBounds(gfx::Size(100, 100));
     test_layer->SetDrawsContent(true);
+    test_layer->SetHitTestable(true);
     test_layer->SetTouchActionRegion(touch_action_region);
     root_layer()->test_properties()->AddChild(std::move(test_layer));
   }
@@ -1434,6 +1473,7 @@
     test_layer->test_properties()->position = gfx::PointF(25.f, 25.f);
     test_layer->SetBounds(gfx::Size(50, 50));
     test_layer->SetDrawsContent(true);
+    test_layer->SetHitTestable(true);
     test_layer->SetTouchActionRegion(touch_action_region);
     root->test_properties()->AddChild(std::move(test_layer));
   }
@@ -1579,6 +1619,7 @@
     child->test_properties()->position = gfx::PointF(-50.f, -50.f);
     child->SetBounds(gfx::Size(300, 300));
     child->SetDrawsContent(true);
+    child->SetHitTestable(true);
     child->SetTouchActionRegion(touch_action_region);
     clipping_layer->test_properties()->AddChild(std::move(child));
     root->test_properties()->AddChild(std::move(clipping_layer));
@@ -1663,6 +1704,7 @@
     child->test_properties()->position = gfx::PointF(-50.f, -50.f);
     child->SetBounds(gfx::Size(300, 300));
     child->SetDrawsContent(true);
+    child->SetHitTestable(true);
     child->SetTouchActionRegion(touch_action_region);
     clipping_layer->test_properties()->AddChild(std::move(child));
     surface->test_properties()->AddChild(std::move(clipping_layer));
@@ -1723,6 +1765,7 @@
     // layer is located.
     touch_layer->SetBounds(gfx::Size(50, 50));
     touch_layer->SetDrawsContent(true);
+    touch_layer->SetHitTestable(true);
     TouchActionRegion touch_action_region;
     touch_action_region.Union(kTouchActionNone, gfx::Rect(0, 0, 50, 50));
     touch_layer->SetTouchActionRegion(touch_action_region);
@@ -1737,6 +1780,7 @@
     notouch_layer->test_properties()->position = gfx::PointF(0, 25);
     notouch_layer->SetBounds(gfx::Size(50, 50));
     notouch_layer->SetDrawsContent(true);
+    notouch_layer->SetHitTestable(true);
     root->test_properties()->AddChild(std::move(notouch_layer));
   }
 
@@ -1789,6 +1833,7 @@
   LayerImpl* root = root_layer();
   root->SetBounds(gfx::Size(100, 100));
   root->SetDrawsContent(true);
+  root->SetHitTestable(true);
   {
     TouchActionRegion touch_action_region;
     touch_action_region.Union(kTouchActionNone, gfx::Rect(10, 10, 30, 30));
@@ -1796,6 +1841,7 @@
         LayerImpl::Create(host_impl().active_tree(), 12345);
     test_layer->SetBounds(gfx::Size(50, 50));
     test_layer->SetDrawsContent(false);
+    test_layer->SetHitTestable(false);
     test_layer->SetTouchActionRegion(touch_action_region);
     root->test_properties()->AddChild(std::move(test_layer));
   }
@@ -2259,6 +2305,7 @@
     root->test_properties()->transform = translate_z;
     root->SetBounds(gfx::Size(100, 100));
     root->SetDrawsContent(true);
+    root->SetHitTestable(true);
   }
   {
     gfx::Transform translate_z;
@@ -2266,6 +2313,7 @@
     left_child->test_properties()->transform = translate_z;
     left_child->SetBounds(gfx::Size(100, 100));
     left_child->SetDrawsContent(true);
+    left_child->SetHitTestable(true);
   }
   {
     gfx::Transform translate_z;
diff --git a/cc/trees/property_tree.cc b/cc/trees/property_tree.cc
index 0ed2742f..9801773 100644
--- a/cc/trees/property_tree.cc
+++ b/cc/trees/property_tree.cc
@@ -830,17 +830,9 @@
   // Reset to false when a node is first met. We'll set the bit later
   // when we actually encounter a masking child.
   node->has_masking_child = false;
-  if (node->blend_mode == SkBlendMode::kDstIn)
+  if (node->blend_mode == SkBlendMode::kDstIn) {
+    DCHECK(parent_node->has_render_surface);
     parent_node->has_masking_child = true;
-}
-
-void EffectTree::UpdateHitTestMayBeAffectedByMask(EffectNode* node,
-                                                  EffectNode* parent_node) {
-  node->hit_test_may_be_affected_by_mask =
-      node->is_masked || node->has_masking_child;
-  if (parent_node) {
-    node->hit_test_may_be_affected_by_mask |=
-        parent_node->hit_test_may_be_affected_by_mask;
   }
 }
 
@@ -920,7 +912,6 @@
   UpdateEffectChanged(node, parent_node);
   UpdateBackfaceVisibility(node, parent_node);
   UpdateHasMaskingChild(node, parent_node);
-  UpdateHitTestMayBeAffectedByMask(node, parent_node);
   UpdateSurfaceContentsScale(node);
 }
 
@@ -1201,8 +1192,11 @@
 
 bool EffectTree::HitTestMayBeAffectedByMask(int effect_id) const {
   const EffectNode* effect_node = Node(effect_id);
-  if (effect_node)
-    return effect_node->hit_test_may_be_affected_by_mask;
+  for (; effect_node->id != kContentsRootNodeId;
+       effect_node = Node(effect_node->target_id)) {
+    if (effect_node->has_masking_child || effect_node->is_masked)
+      return true;
+  }
   return false;
 }
 
diff --git a/cc/trees/property_tree.h b/cc/trees/property_tree.h
index 5fa178a..3296420 100644
--- a/cc/trees/property_tree.h
+++ b/cc/trees/property_tree.h
@@ -381,8 +381,6 @@
   void UpdateIsDrawn(EffectNode* node, EffectNode* parent_node);
   void UpdateBackfaceVisibility(EffectNode* node, EffectNode* parent_node);
   void UpdateHasMaskingChild(EffectNode* node, EffectNode* parent_node);
-  void UpdateHitTestMayBeAffectedByMask(EffectNode* node,
-                                        EffectNode* parent_node);
 
   // Stores copy requests, keyed by node id.
   std::unordered_multimap<int, std::unique_ptr<viz::CopyOutputRequest>>
diff --git a/chrome/android/BUILD.gn b/chrome/android/BUILD.gn
index d6915c96..b278406 100644
--- a/chrome/android/BUILD.gn
+++ b/chrome/android/BUILD.gn
@@ -408,6 +408,7 @@
     "//components/supervised_user_error_page:enums_srcjar",
     "//components/ui_metrics:ui_metrics_enums_java",
     "//chrome/browser/ui:tab_model_enums_java",
+    "//net:effective_connection_type_java",
     ":vr_build_config",
   ]
 
@@ -2053,7 +2054,7 @@
   }
 }
 
-template("monochrome_public_bundle_tmpl") {
+template("monochrome_or_trichrome_public_bundle_tmpl") {
   _base_module_target_name = "${invoker.target_name}__base_bundle_module"
   _is_trichrome =
       defined(invoker.use_trichrome_library) && invoker.use_trichrome_library
@@ -2066,15 +2067,25 @@
   }
 
   if (_is_trichrome) {
-    _version_code = trichrome_version_code
-  } else if (defined(invoker.is_64_bit_browser) && invoker.is_64_bit_browser) {
-    if (invoker.build_apk_secondary_abi && invoker.include_32_bit_webview) {
-      _version_code = monochrome_64_32_version_code
+    if (defined(invoker.is_64_bit_browser) && invoker.is_64_bit_browser) {
+      if (invoker.build_apk_secondary_abi && invoker.include_32_bit_webview) {
+        _version_code = trichrome_64_32_version_code
+      } else {
+        _version_code = trichrome_64_version_code
+      }
     } else {
-      _version_code = monochrome_64_version_code
+      _version_code = trichrome_version_code
     }
   } else {
-    _version_code = monochrome_version_code
+    if (defined(invoker.is_64_bit_browser) && invoker.is_64_bit_browser) {
+      if (invoker.build_apk_secondary_abi && invoker.include_32_bit_webview) {
+        _version_code = monochrome_64_32_version_code
+      } else {
+        _version_code = monochrome_64_version_code
+      }
+    } else {
+      _version_code = monochrome_version_code
+    }
   }
   _version_name = chrome_version_name
 
@@ -2172,7 +2183,7 @@
   }
 }
 
-monochrome_public_bundle_tmpl("monochrome_public_bundle") {
+monochrome_or_trichrome_public_bundle_tmpl("monochrome_public_bundle") {
   bundle_suffix = ""
 }
 
@@ -2186,8 +2197,13 @@
   }
 }
 
+monochrome_or_trichrome_public_bundle_tmpl("trichrome_chrome_bundle") {
+  bundle_suffix = ""
+  use_trichrome_library = true
+}
+
 if (android_64bit_target_cpu) {
-  monochrome_public_bundle_tmpl("monochrome_64_public_bundle") {
+  monochrome_or_trichrome_public_bundle_tmpl("monochrome_64_public_bundle") {
     bundle_suffix = "64"
     is_64_bit_browser = true
     if (build_apk_secondary_abi) {
@@ -2195,18 +2211,31 @@
     }
   }
 
-  monochrome_public_bundle_tmpl("monochrome_64_32_public_bundle") {
+  monochrome_or_trichrome_public_bundle_tmpl("monochrome_64_32_public_bundle") {
     bundle_suffix = "6432"
     is_64_bit_browser = true
     if (build_apk_secondary_abi) {
       include_32_bit_webview = true
     }
   }
-}
 
-monochrome_public_bundle_tmpl("trichrome_chrome_bundle") {
-  bundle_suffix = ""
-  use_trichrome_library = true
+  monochrome_or_trichrome_public_bundle_tmpl("trichrome_64_chrome_bundle") {
+    bundle_suffix = "64"
+    is_64_bit_browser = true
+    use_trichrome_library = true
+    if (build_apk_secondary_abi) {
+      include_32_bit_webview = false
+    }
+  }
+
+  monochrome_or_trichrome_public_bundle_tmpl("trichrome_64_32_chrome_bundle") {
+    bundle_suffix = "6432"
+    is_64_bit_browser = true
+    use_trichrome_library = true
+    if (build_apk_secondary_abi) {
+      include_32_bit_webview = true
+    }
+  }
 }
 
 generate_jni("chrome_jni_headers") {
@@ -2363,6 +2392,7 @@
     "java/src/org/chromium/chrome/browser/metrics/UmaUtils.java",
     "java/src/org/chromium/chrome/browser/metrics/VariationsSession.java",
     "java/src/org/chromium/chrome/browser/mojo/ChromeInterfaceRegistrar.java",
+    "java/src/org/chromium/chrome/browser/net/nqe/NetworkQualityProvider.java",
     "java/src/org/chromium/chrome/browser/net/spdyproxy/DataReductionProxySettings.java",
     "java/src/org/chromium/chrome/browser/notifications/ActionInfo.java",
     "java/src/org/chromium/chrome/browser/notifications/NotificationPlatformBridge.java",
diff --git a/chrome/android/chrome_java_sources.gni b/chrome/android/chrome_java_sources.gni
index 475f705..0f2aa90f 100644
--- a/chrome/android/chrome_java_sources.gni
+++ b/chrome/android/chrome_java_sources.gni
@@ -944,6 +944,8 @@
   "java/src/org/chromium/chrome/browser/native_page/NativePageHost.java",
   "java/src/org/chromium/chrome/browser/native_page/NativePageNavigationDelegate.java",
   "java/src/org/chromium/chrome/browser/native_page/NativePageNavigationDelegateImpl.java",
+  "java/src/org/chromium/chrome/browser/net/nqe/NetworkQualityObserver.java",
+  "java/src/org/chromium/chrome/browser/net/nqe/NetworkQualityProvider.java",
   "java/src/org/chromium/chrome/browser/net/spdyproxy/DataReductionProxySettings.java",
   "java/src/org/chromium/chrome/browser/nfc/BeamCallback.java",
   "java/src/org/chromium/chrome/browser/nfc/BeamController.java",
diff --git a/chrome/android/chrome_junit_test_java_sources.gni b/chrome/android/chrome_junit_test_java_sources.gni
index 3650ab8..17b1e3a2 100644
--- a/chrome/android/chrome_junit_test_java_sources.gni
+++ b/chrome/android/chrome_junit_test_java_sources.gni
@@ -114,6 +114,7 @@
   "junit/src/org/chromium/chrome/browser/media/ui/MediaNotificationTitleUpdatedTest.java",
   "junit/src/org/chromium/chrome/browser/metrics/VariationsSessionTest.java",
   "junit/src/org/chromium/chrome/browser/native_page/NativePageFactoryTest.java",
+  "junit/src/org/chromium/chrome/browser/net/nqe/NetworkQualityProviderTest.java",
   "junit/src/org/chromium/chrome/browser/notifications/NotificationPlatformBridgeUnitTest.java",
   "junit/src/org/chromium/chrome/browser/notifications/NotificationSystemStatusUtilUnitTest.java",
   "junit/src/org/chromium/chrome/browser/notifications/NotificationTriggerBackgroundTaskTest.java",
diff --git a/chrome/android/chrome_public_apk_tmpl.gni b/chrome/android/chrome_public_apk_tmpl.gni
index a244711c..f8fa973 100644
--- a/chrome/android/chrome_public_apk_tmpl.gni
+++ b/chrome/android/chrome_public_apk_tmpl.gni
@@ -366,7 +366,9 @@
       # depend on their respective versions of the shared library APK even
       # though they're functionally the same.
       native_lib_placeholders = [ "libdummy.so" ]
-      if (android_64bit_target_cpu && build_apk_secondary_abi) {
+      if (android_64bit_target_cpu && build_apk_secondary_abi &&
+          (!defined(invoker.is_64_bit_browser) || !invoker.is_64_bit_browser ||
+           invoker.include_32_bit_webview)) {
         secondary_native_lib_placeholders = [ "libdummy.so" ]
       }
       _pak_prefix = "trichrome_chrome"
diff --git a/chrome/android/java/AndroidManifest.xml b/chrome/android/java/AndroidManifest.xml
index 07aa8ed9..77eaf16 100644
--- a/chrome/android/java/AndroidManifest.xml
+++ b/chrome/android/java/AndroidManifest.xml
@@ -132,6 +132,9 @@
         android:largeHeap="false"
         android:manageSpaceActivity="@string/manage_space_activity"
         android:supportsRtl="true"
+        {% if (use_zygote|default(false) == 'true') %}
+        android:zygotePreloadName="org.chromium.content.app.ZygotePreload"
+        {% endif %}
         {% if backup_key is defined %}
         android:allowBackup="true"
         android:backupAgent="org.chromium.chrome.browser.ChromeBackupAgent"
@@ -1169,6 +1172,9 @@
             android:permission="{{ manifest_package }}.permission.CHILD_SERVICE"
             android:isolatedProcess="true"
             android:exported="{{sandboxed_service_exported|default(false)}}"
+            {% if (use_zygote|default(false) == 'true') %}
+            android:useAppZygote="true"
+            {% endif %}
             {% if (sandboxed_service_exported|default(false)) == 'true' %}
             android:externalService="true"
             tools:ignore="ExportedService"
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/AppHooks.java b/chrome/android/java/src/org/chromium/chrome/browser/AppHooks.java
index fdb0d04..1d1c050 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/AppHooks.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/AppHooks.java
@@ -459,4 +459,9 @@
     public @Nullable ImmersiveModeManager createImmersiveModeManager(View contentView) {
         return null;
     }
+
+    /**
+     * Starts monitoring network quality. Must be called after native initialization is complete.
+     */
+    public void startMonitoringNetworkQuality() {}
 }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/ChromeActivity.java b/chrome/android/java/src/org/chromium/chrome/browser/ChromeActivity.java
index ed5dbc4..39b6d9e 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/ChromeActivity.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/ChromeActivity.java
@@ -1511,6 +1511,7 @@
                     !ChromeFeatureList.isEnabled(ChromeFeatureList.CONTEXTUAL_SUGGESTIONS_BUTTON));
             getComponent().resolveContextualSuggestionsCoordinator();
         }
+        AppHooks.get().startMonitoringNetworkQuality();
     }
 
     /**
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/ChromeFeatureList.java b/chrome/android/java/src/org/chromium/chrome/browser/ChromeFeatureList.java
index 43002e5..2ada68d0 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/ChromeFeatureList.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/ChromeFeatureList.java
@@ -284,6 +284,8 @@
             "OmniboxUIExperimentHideSteadyStateUrlTrivialSubdomains";
     public static final String OMNIBOX_NEW_ANSWER_LAYOUT = "OmniboxNewAnswerLayout";
     public static final String OMNIBOX_RICH_ENTITY_SUGGESTIONS = "OmniboxRichEntitySuggestions";
+    public static final String OMNIBOX_SHOW_SUGGESTION_FAVICONS =
+            "OmniboxUIExperimentShowSuggestionFavicons";
     public static final String OMNIBOX_SPARE_RENDERER = "OmniboxSpareRenderer";
     public static final String OVERSCROLL_HISTORY_NAVIGATION = "OverscrollHistoryNavigation";
     public static final String PAY_WITH_GOOGLE_V1 = "PayWithGoogleV1";
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/MonochromeApplication.java b/chrome/android/java/src/org/chromium/chrome/browser/MonochromeApplication.java
index dc0535f..cd3f330 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/MonochromeApplication.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/MonochromeApplication.java
@@ -26,7 +26,9 @@
     @Override
     public void onCreate() {
         super.onCreate();
-        LibraryLoader.getInstance().setNativeLibraryPreloader(new MonochromeLibraryPreloader());
+        if (!LibraryLoader.getInstance().isLoadedByZygote()) {
+            LibraryLoader.getInstance().setNativeLibraryPreloader(new MonochromeLibraryPreloader());
+        }
         // ChildProcessCreationParams is only needed for browser process, though it is
         // created and set in all processes. We must set isExternalService to true for
         // Monochrome because Monochrome's renderer services are shared with WebView
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/explore_sites/CategoryCardAdapter.java b/chrome/android/java/src/org/chromium/chrome/browser/explore_sites/CategoryCardAdapter.java
index f7a9556..0a0006d6 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/explore_sites/CategoryCardAdapter.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/explore_sites/CategoryCardAdapter.java
@@ -52,7 +52,7 @@
     private LinearLayoutManager mLayoutManager;
     private PropertyModel mCategoryModel;
 
-    public CategoryCardAdapter(PropertyModel model, LinearLayoutManager layoutManager,
+    CategoryCardAdapter(PropertyModel model, LinearLayoutManager layoutManager,
             RoundedIconGenerator iconGenerator, ContextMenuManager contextMenuManager,
             NativePageNavigationDelegate navDelegate, Profile profile) {
         mCategoryModel = model;
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/explore_sites/ExploreSitesCategoryCardView.java b/chrome/android/java/src/org/chromium/chrome/browser/explore_sites/ExploreSitesCategoryCardView.java
index b8ea3699..4fbf792 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/explore_sites/ExploreSitesCategoryCardView.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/explore_sites/ExploreSitesCategoryCardView.java
@@ -6,6 +6,7 @@
 
 import android.content.Context;
 import android.graphics.Bitmap;
+import android.graphics.Rect;
 import android.util.AttributeSet;
 import android.view.ContextMenu;
 import android.view.LayoutInflater;
@@ -50,11 +51,12 @@
     private int mCategoryCardIndex;
 
     private class CategoryCardInteractionDelegate
-            implements ContextMenuManager.Delegate, OnClickListener, OnCreateContextMenuListener {
+            implements ContextMenuManager.Delegate, OnClickListener, OnCreateContextMenuListener,
+                       OnFocusChangeListener {
         private String mSiteUrl;
         private int mTileIndex;
 
-        public CategoryCardInteractionDelegate(String siteUrl, int tileIndex) {
+        CategoryCardInteractionDelegate(String siteUrl, int tileIndex) {
             mSiteUrl = siteUrl;
             mTileIndex = tileIndex;
         }
@@ -99,14 +101,19 @@
 
         @Override
         public boolean isItemSupported(@ContextMenuManager.ContextMenuItemId int menuItemId) {
-            if (menuItemId == ContextMenuManager.ContextMenuItemId.LEARN_MORE) {
-                return false;
-            }
-            return true;
+            return menuItemId != ContextMenuManager.ContextMenuItemId.LEARN_MORE;
         }
 
         @Override
-        public void onContextMenuCreated(){};
+        public void onContextMenuCreated() {}
+
+        @Override
+        public void onFocusChange(View v, boolean hasFocus) {
+            if (hasFocus) {
+                getParent().requestChildRectangleOnScreen(
+                        ExploreSitesCategoryCardView.this, new Rect(), true);
+            }
+        }
     }
 
     // We use the MVC paradigm for the site tiles inside the category card.  We don't use the MVC
@@ -130,6 +137,7 @@
                                 model.get(ExploreSitesSite.TILE_INDEX_KEY));
                 view.setOnClickListener(interactionDelegate);
                 view.setOnCreateContextMenuListener(interactionDelegate);
+                view.setOnFocusChangeListener(interactionDelegate);
             }
         }
     }
@@ -157,8 +165,8 @@
         mCategoryCardIndex = categoryCardIndex;
         mCategory = category;
 
-        updateTitle(category.getTitle());
-        updateTileViews(category);
+        updateTitle(mCategory.getTitle());
+        updateTileViews(mCategory);
     }
 
     public void updateTitle(String categoryTitle) {
@@ -232,8 +240,8 @@
 
     /**
      * Records UMA data for how far down the EoS page the picked tile was.
-     * @param cardNumber The number card (zero based) of the tile that was picked.
-     * @param tileNumber The number of the tile within the card.
+     * @param cardIndex The number card (zero based) of the tile that was picked.
+     * @param tileIndex The number of the tile within the card.
      */
     public static void recordTileIndexClick(int cardIndex, int tileIndex) {
         // TODO(petewil): Should I get the number of sites in this category from the model instead
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/explore_sites/ExploreSitesPage.java b/chrome/android/java/src/org/chromium/chrome/browser/explore_sites/ExploreSitesPage.java
index 05e30fc7..f8ca689 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/explore_sites/ExploreSitesPage.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/explore_sites/ExploreSitesPage.java
@@ -168,7 +168,7 @@
         mHasFetchedNetworkCatalog = false;
 
         mModel = new PropertyModel.Builder(STATUS_KEY, SCROLL_TO_CATEGORY_KEY, CATEGORY_LIST_KEY)
-                         .with(CATEGORY_LIST_KEY, new ListModel<ExploreSitesCategory>())
+                         .with(CATEGORY_LIST_KEY, new ListModel<>())
                          .with(STATUS_KEY, CatalogLoadingState.LOADING)
                          .build();
 
@@ -194,7 +194,7 @@
         CategoryCardAdapter adapterDelegate = new CategoryCardAdapter(
                 mModel, mLayoutManager, iconGenerator, mContextMenuManager, navDelegate, mProfile);
 
-        mRecyclerView = (RecyclerView) mView.findViewById(R.id.explore_sites_category_recycler);
+        mRecyclerView = mView.findViewById(R.id.explore_sites_category_recycler);
         RecyclerViewAdapter<CategoryCardViewHolderFactory.CategoryCardViewHolder, Void> adapter =
                 new RecyclerViewAdapter<>(adapterDelegate, new CategoryCardViewHolderFactory());
 
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/net/nqe/NetworkQualityObserver.java b/chrome/android/java/src/org/chromium/chrome/browser/net/nqe/NetworkQualityObserver.java
new file mode 100644
index 0000000..0cc4910
--- /dev/null
+++ b/chrome/android/java/src/org/chromium/chrome/browser/net/nqe/NetworkQualityObserver.java
@@ -0,0 +1,32 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.chrome.browser.net.nqe;
+
+import org.chromium.net.EffectiveConnectionType;
+
+/**
+ * Interface for observing changes to the current Network Quality Estimate.
+ */
+public interface NetworkQualityObserver {
+    /**
+     * Called when there is a change in the effective connection type.
+     *
+     * @param effectiveConnectionType the current effective connection type.
+     */
+    default void onEffectiveConnectionTypeChanged(
+            @EffectiveConnectionType int effectiveConnectionType) {}
+
+    /**
+     * Called when there is a substantial change in either HTTP RTT, transport RTT or downstream
+     * throughput estimate.
+     *
+     * @param httpRTTMillis estimate of the round trip time at the http layer.
+     * @param transportRTTMillis estimate of the round trip time at the transport layer.
+     * @param downstreamThroughputKbps estimate of the downstream throughput in Kbps (Kilobits per
+     *            second).
+     */
+    default void onRTTOrThroughputEstimatesComputed(
+            long httpRTTMillis, long transportRTTMillis, int downstreamThroughputKbps) {}
+}
\ No newline at end of file
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/net/nqe/NetworkQualityProvider.java b/chrome/android/java/src/org/chromium/chrome/browser/net/nqe/NetworkQualityProvider.java
new file mode 100644
index 0000000..669ce7ca
--- /dev/null
+++ b/chrome/android/java/src/org/chromium/chrome/browser/net/nqe/NetworkQualityProvider.java
@@ -0,0 +1,100 @@
+// 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.chrome.browser.net.nqe;
+
+import org.chromium.base.ObserverList;
+import org.chromium.base.ObserverList.RewindableIterator;
+import org.chromium.base.annotations.CalledByNative;
+import org.chromium.base.library_loader.LibraryProcessType;
+import org.chromium.content_public.browser.BrowserStartupController;
+import org.chromium.net.EffectiveConnectionType;
+
+/**
+ * Provides Network Quality Estimates to observers.
+ */
+public class NetworkQualityProvider {
+    protected static NetworkQualityProvider sInstance;
+
+    private final ObserverList<NetworkQualityObserver> mObservers = new ObserverList<>();
+
+    private final RewindableIterator<NetworkQualityObserver> mRewindableIterator;
+
+    private boolean mInitializedRtt;
+    private long mHttpRttMillis;
+    private long mTransportRttMillis;
+    private int mDownstreamThroughputKbps;
+    private @EffectiveConnectionType Integer mEffectiveConnectionType;
+
+    private long mNativeNetworkQualityProvider;
+
+    /**
+     * @param observer The {@link NetworkQualityObserver} to be called when connection changes
+     *            occur. This will trigger all observer callbacks for which data is already
+     *            available.
+     */
+    public static void addObserverAndMaybeTrigger(NetworkQualityObserver observer) {
+        NetworkQualityProvider provider = getInstance();
+        provider.mObservers.addObserver(observer);
+        if (provider.mEffectiveConnectionType != null) {
+            observer.onEffectiveConnectionTypeChanged(provider.mEffectiveConnectionType);
+        }
+        if (provider.mInitializedRtt) {
+            observer.onRTTOrThroughputEstimatesComputed(provider.mHttpRttMillis,
+                    provider.mTransportRttMillis, provider.mDownstreamThroughputKbps);
+        }
+    }
+
+    /**
+     * @param observer The {@link NetworkQualityObserver} to remove.
+     */
+    public static void removeObserver(NetworkQualityObserver observer) {
+        NetworkQualityProvider provider = getInstance();
+        provider.mObservers.removeObserver(observer);
+    }
+
+    private static NetworkQualityProvider getInstance() {
+        if (sInstance == null) sInstance = new NetworkQualityProvider();
+        return sInstance;
+    }
+
+    // Protected for testing.
+    protected NetworkQualityProvider() {
+        sInstance = this;
+        mRewindableIterator = mObservers.rewindableIterator();
+        doNativeInit();
+    }
+
+    protected void doNativeInit() {
+        assert BrowserStartupController.get(LibraryProcessType.PROCESS_BROWSER)
+                .isStartupSuccessfullyCompleted();
+        mNativeNetworkQualityProvider = nativeInit();
+    }
+
+    @CalledByNative
+    public void onEffectiveConnectionTypeChanged(
+            @EffectiveConnectionType int effectiveConnectionType) {
+        mEffectiveConnectionType = effectiveConnectionType;
+        mRewindableIterator.rewind();
+        while (mRewindableIterator.hasNext()) {
+            mRewindableIterator.next().onEffectiveConnectionTypeChanged(mEffectiveConnectionType);
+        }
+    }
+
+    @CalledByNative
+    public void onRTTOrThroughputEstimatesComputed(
+            long httpRTTMillis, long transportRTTMillis, int downstreamThroughputKbps) {
+        mHttpRttMillis = httpRTTMillis;
+        mTransportRttMillis = transportRTTMillis;
+        mDownstreamThroughputKbps = downstreamThroughputKbps;
+        mInitializedRtt = true;
+        mRewindableIterator.rewind();
+        while (mRewindableIterator.hasNext()) {
+            mRewindableIterator.next().onRTTOrThroughputEstimatesComputed(
+                    mHttpRttMillis, mTransportRttMillis, mDownstreamThroughputKbps);
+        }
+    }
+
+    private native long nativeInit();
+}
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/net/qualityprovider/OWNERS b/chrome/android/java/src/org/chromium/chrome/browser/net/nqe/OWNERS
similarity index 100%
rename from chrome/android/java/src/org/chromium/chrome/browser/net/qualityprovider/OWNERS
rename to chrome/android/java/src/org/chromium/chrome/browser/net/nqe/OWNERS
diff --git a/chrome/android/junit/src/org/chromium/chrome/browser/net/nqe/NetworkQualityProviderTest.java b/chrome/android/junit/src/org/chromium/chrome/browser/net/nqe/NetworkQualityProviderTest.java
new file mode 100644
index 0000000..5208c50
--- /dev/null
+++ b/chrome/android/junit/src/org/chromium/chrome/browser/net/nqe/NetworkQualityProviderTest.java
@@ -0,0 +1,124 @@
+// 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.chrome.browser.net.nqe;
+
+import static org.junit.Assert.assertEquals;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.robolectric.annotation.Config;
+
+import org.chromium.base.test.BaseRobolectricTestRunner;
+import org.chromium.net.EffectiveConnectionType;
+
+/**
+ * JUnit tests for NetworkQualityProvider which run against Robolectric.
+ */
+@RunWith(BaseRobolectricTestRunner.class)
+@Config(manifest = Config.NONE)
+public class NetworkQualityProviderTest {
+    private static final long HTTP_RTT = 1;
+    private static final long TRANSPORT_RTT = 2;
+    private static final int KBPS = 3;
+    private static final @EffectiveConnectionType int CONNECTION_TYPE = 4;
+
+    private NetworkQualityProvider mProvider;
+    private TestNetworkQualityObserver mObserver;
+
+    private static class NetworkQualityProviderForTesting extends NetworkQualityProvider {
+        @Override
+        protected void doNativeInit() {}
+
+        public static void reset(NetworkQualityProvider provider) {
+            sInstance = provider;
+        }
+    }
+
+    private static class TestNetworkQualityObserver implements NetworkQualityObserver {
+        private int mConnectionTypeCount;
+        private int mRTTCount;
+
+        public int getConnectionTypeCount() {
+            return mConnectionTypeCount;
+        }
+
+        public int getRTTCount() {
+            return mRTTCount;
+        }
+
+        @Override
+        public void onEffectiveConnectionTypeChanged(
+                @EffectiveConnectionType int effectiveConnectionType) {
+            ++mConnectionTypeCount;
+            assertEquals(CONNECTION_TYPE, effectiveConnectionType);
+        }
+
+        @Override
+        public void onRTTOrThroughputEstimatesComputed(
+                long httpRTTMillis, long transportRTTMillis, int downstreamThroughputKbps) {
+            ++mRTTCount;
+            assertEquals(HTTP_RTT, httpRTTMillis);
+            assertEquals(TRANSPORT_RTT, transportRTTMillis);
+            assertEquals(KBPS, downstreamThroughputKbps);
+        }
+    }
+
+    @Before
+    public void setUp() throws Exception {
+        mProvider = new NetworkQualityProviderForTesting();
+        NetworkQualityProviderForTesting.reset(mProvider);
+        mObserver = new TestNetworkQualityObserver();
+    }
+
+    @Test
+    public void testObserversGetNotified() throws Exception {
+        NetworkQualityProvider.addObserverAndMaybeTrigger(mObserver);
+
+        assertEquals(0, mObserver.getConnectionTypeCount());
+        assertEquals(0, mObserver.getRTTCount());
+
+        mProvider.onEffectiveConnectionTypeChanged(CONNECTION_TYPE);
+        assertEquals(1, mObserver.getConnectionTypeCount());
+        assertEquals(0, mObserver.getRTTCount());
+
+        mProvider.onRTTOrThroughputEstimatesComputed(HTTP_RTT, TRANSPORT_RTT, KBPS);
+        assertEquals(1, mObserver.getConnectionTypeCount());
+        assertEquals(1, mObserver.getRTTCount());
+    }
+
+    @Test
+    public void testRemovedObserversDontGetNotified() throws Exception {
+        NetworkQualityProvider.addObserverAndMaybeTrigger(mObserver);
+        NetworkQualityProvider.removeObserver(mObserver);
+
+        mProvider.onEffectiveConnectionTypeChanged(CONNECTION_TYPE);
+        mProvider.onRTTOrThroughputEstimatesComputed(HTTP_RTT, TRANSPORT_RTT, KBPS);
+        assertEquals(0, mObserver.getConnectionTypeCount());
+        assertEquals(0, mObserver.getRTTCount());
+    }
+
+    @Test
+    public void testObserversGetNotifiedWhenAdded() throws Exception {
+        mProvider.onEffectiveConnectionTypeChanged(CONNECTION_TYPE);
+        NetworkQualityProvider.addObserverAndMaybeTrigger(mObserver);
+        assertEquals(1, mObserver.getConnectionTypeCount());
+        assertEquals(0, mObserver.getRTTCount());
+
+        NetworkQualityProvider.removeObserver(mObserver);
+        mProvider.onRTTOrThroughputEstimatesComputed(HTTP_RTT, TRANSPORT_RTT, KBPS);
+        NetworkQualityProvider.addObserverAndMaybeTrigger(mObserver);
+        assertEquals(2, mObserver.getConnectionTypeCount());
+        assertEquals(1, mObserver.getRTTCount());
+        NetworkQualityProvider.removeObserver(mObserver);
+
+        mProvider = new NetworkQualityProviderForTesting();
+        NetworkQualityProviderForTesting.reset(mProvider);
+        mProvider.onRTTOrThroughputEstimatesComputed(HTTP_RTT, TRANSPORT_RTT, KBPS);
+        NetworkQualityProvider.addObserverAndMaybeTrigger(mObserver);
+        assertEquals(2, mObserver.getConnectionTypeCount());
+        assertEquals(2, mObserver.getRTTCount());
+    }
+}
\ No newline at end of file
diff --git a/chrome/android/webapk/shell_apk/junit/src/org/chromium/webapk/shell_apk/h2o/LaunchTest.java b/chrome/android/webapk/shell_apk/junit/src/org/chromium/webapk/shell_apk/h2o/LaunchTest.java
index 7270a127..cc42488 100644
--- a/chrome/android/webapk/shell_apk/junit/src/org/chromium/webapk/shell_apk/h2o/LaunchTest.java
+++ b/chrome/android/webapk/shell_apk/junit/src/org/chromium/webapk/shell_apk/h2o/LaunchTest.java
@@ -257,8 +257,7 @@
         // the host browser to relaunch it again.
         {
             SharedPreferences.Editor editor = WebApkSharedPreferences.getPrefs(mAppContext).edit();
-            editor.putLong(
-                    WebApkSharedPreferences.SHARED_PREF_REQUEST_HOST_BROWSER_RELAUNCH_TIMESTAMP,
+            editor.putLong(WebApkSharedPreferences.PREF_REQUEST_HOST_BROWSER_RELAUNCH_TIMESTAMP,
                     System.currentTimeMillis() - 1);
             editor.apply();
 
@@ -272,8 +271,7 @@
         // the host browser to relaunch it.
         {
             SharedPreferences.Editor editor = WebApkSharedPreferences.getPrefs(mAppContext).edit();
-            editor.putLong(
-                    WebApkSharedPreferences.SHARED_PREF_REQUEST_HOST_BROWSER_RELAUNCH_TIMESTAMP, 1);
+            editor.putLong(WebApkSharedPreferences.PREF_REQUEST_HOST_BROWSER_RELAUNCH_TIMESTAMP, 1);
             editor.apply();
 
             Robolectric.buildActivity(H2OMainActivity.class, launchIntent).create();
diff --git a/chrome/android/webapk/shell_apk/src/org/chromium/webapk/shell_apk/WebApkSharedPreferences.java b/chrome/android/webapk/shell_apk/src/org/chromium/webapk/shell_apk/WebApkSharedPreferences.java
index 4faf0b3..0ef7f74 100644
--- a/chrome/android/webapk/shell_apk/src/org/chromium/webapk/shell_apk/WebApkSharedPreferences.java
+++ b/chrome/android/webapk/shell_apk/src/org/chromium/webapk/shell_apk/WebApkSharedPreferences.java
@@ -28,7 +28,7 @@
             "org.chromium.webapk.shell_apk.dex_version";
 
     /** Timestamp of when the WebAPK asked the host browser to relaunch the WebAPK. */
-    public static final String SHARED_PREF_REQUEST_HOST_BROWSER_RELAUNCH_TIMESTAMP =
+    public static final String PREF_REQUEST_HOST_BROWSER_RELAUNCH_TIMESTAMP =
             "org.chromium.webapk.shell_apk.request_host_browser_relaunch_timestamp";
 
     public static SharedPreferences getPrefs(Context context) {
diff --git a/chrome/android/webapk/shell_apk/src/org/chromium/webapk/shell_apk/h2o/H2OLauncher.java b/chrome/android/webapk/shell_apk/src/org/chromium/webapk/shell_apk/h2o/H2OLauncher.java
index 821902a..b870432 100644
--- a/chrome/android/webapk/shell_apk/src/org/chromium/webapk/shell_apk/h2o/H2OLauncher.java
+++ b/chrome/android/webapk/shell_apk/src/org/chromium/webapk/shell_apk/h2o/H2OLauncher.java
@@ -42,7 +42,7 @@
         SharedPreferences sharedPrefs = WebApkSharedPreferences.getPrefs(context);
         long now = System.currentTimeMillis();
         long lastRequestTimestamp = sharedPrefs.getLong(
-                WebApkSharedPreferences.SHARED_PREF_REQUEST_HOST_BROWSER_RELAUNCH_TIMESTAMP, -1);
+                WebApkSharedPreferences.PREF_REQUEST_HOST_BROWSER_RELAUNCH_TIMESTAMP, -1);
         return (now - lastRequestTimestamp) <= deltaMs;
     }
 
@@ -102,8 +102,8 @@
             Context context, HostBrowserLauncherParams params) {
         long timestamp = System.currentTimeMillis();
         SharedPreferences.Editor editor = WebApkSharedPreferences.getPrefs(context).edit();
-        editor.putLong(WebApkSharedPreferences.SHARED_PREF_REQUEST_HOST_BROWSER_RELAUNCH_TIMESTAMP,
-                timestamp);
+        editor.putLong(
+                WebApkSharedPreferences.PREF_REQUEST_HOST_BROWSER_RELAUNCH_TIMESTAMP, timestamp);
         editor.apply();
 
         Bundle extraExtras = new Bundle();
diff --git a/chrome/app/generated_resources.grd b/chrome/app/generated_resources.grd
index 1d60c07..4e1f4f0 100644
--- a/chrome/app/generated_resources.grd
+++ b/chrome/app/generated_resources.grd
@@ -9536,6 +9536,15 @@
       <message name="IDS_WEBAUTHN_MISSING_USER_VERIFICATION_DESC" desc="The description on a dialog informing the user that the security key (an external physical device for user authentication) that they selected does not support any methods of identifying its owner, e.g. a fingerprint reader or a secret code (PIN).">
         That security key does not support any method of personal identification (e.g. a fingerprint reader, or PIN entry), but that feature was requested.
       </message>
+      <message name="IDS_WEBAUTHN_REQUEST_ATTESTATION_PERMISSION_TITLE" desc="Title of a dialog informing the user that the website wants to see information that identifies the user's Security Key, such as make and model number. The 'make' of a device is the brand name of the manufacturer, e.g. Yubikey is a make of Security Key. The 'model' of a device is the specific product, e.g. Yubikey Neo is a model of Security Key.">
+        Give permission to read your Security Key?
+      </message>
+      <message name="IDS_WEBAUTHN_REQUEST_ATTESTATION_PERMISSION_DESC" desc="The description on a dialog informing the user that the website wants to see information that identifies the user's Security Key, such as make and model number. The 'make' of a device is the brand name of the manufacturer, e.g. Yubikey is a make of Security Key. The 'model' of a device is the specific product, e.g. Yubikey Neo is a model of Security Key.">
+        <ph name="WEBSITE"><ex>accounts.google.com</ex>$1</ph> wants to see the make and model of your security key
+      </message>
+      <message name="IDS_WEBAUTHN_ALLOW_ATTESTATION" desc="Label on button to allow a website to see information that identifies the user's Security Key, such as make and model number. The 'make' of a device is the brand name of the manufacturer, e.g. Yubikey is a make of Security Key. The 'model' of a device is the specific product, e.g. Yubikey Neo is a model of Security Key.">
+        Allow
+      </message>
    </if>
    <if expr="is_macosx">
      <message name="IDS_WEBAUTHN_TOUCH_ID_TITLE" desc="Title of the dialog shown when the user tries to sign in with Touch ID." meaning="'Touch ID' is the fingerprint recognition feature in macOS. Try to refer Apple support documentation in the target language for the appropriate product name translation.">
diff --git a/chrome/app/generated_resources_grd/IDS_WEBAUTHN_ALLOW_ATTESTATION.png.sha1 b/chrome/app/generated_resources_grd/IDS_WEBAUTHN_ALLOW_ATTESTATION.png.sha1
new file mode 100644
index 0000000..646577a
--- /dev/null
+++ b/chrome/app/generated_resources_grd/IDS_WEBAUTHN_ALLOW_ATTESTATION.png.sha1
@@ -0,0 +1 @@
+3975694e13a91a3ef1e4fd4276c7bd3c462b1bee
\ No newline at end of file
diff --git a/chrome/app/generated_resources_grd/IDS_WEBAUTHN_REQUEST_ATTESTATION_PERMISSION_DESC.png.sha1 b/chrome/app/generated_resources_grd/IDS_WEBAUTHN_REQUEST_ATTESTATION_PERMISSION_DESC.png.sha1
new file mode 100644
index 0000000..646577a
--- /dev/null
+++ b/chrome/app/generated_resources_grd/IDS_WEBAUTHN_REQUEST_ATTESTATION_PERMISSION_DESC.png.sha1
@@ -0,0 +1 @@
+3975694e13a91a3ef1e4fd4276c7bd3c462b1bee
\ No newline at end of file
diff --git a/chrome/app/generated_resources_grd/IDS_WEBAUTHN_REQUEST_ATTESTATION_PERMISSION_TITLE.png.sha1 b/chrome/app/generated_resources_grd/IDS_WEBAUTHN_REQUEST_ATTESTATION_PERMISSION_TITLE.png.sha1
new file mode 100644
index 0000000..646577a
--- /dev/null
+++ b/chrome/app/generated_resources_grd/IDS_WEBAUTHN_REQUEST_ATTESTATION_PERMISSION_TITLE.png.sha1
@@ -0,0 +1 @@
+3975694e13a91a3ef1e4fd4276c7bd3c462b1bee
\ No newline at end of file
diff --git a/chrome/app/vector_icons/google_chrome/google_g_logo.icon b/chrome/app/vector_icons/google_chrome/google_g_logo.icon
deleted file mode 100644
index ba12830..0000000
--- a/chrome/app/vector_icons/google_chrome/google_g_logo.icon
+++ /dev/null
@@ -1,48 +0,0 @@
-// Copyright 2017 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-CANVAS_DIMENSIONS, 32,
-PATH_COLOR_ARGB, 0xFF, 0x42, 0x85, 0xF4,
-MOVE_TO, 31.9f, 16.36f,
-R_CUBIC_TO, 0, -1.13f, -0.1f, -2.22f, -0.3f, -3.27f,
-H_LINE_TO, 16.27f,
-R_V_LINE_TO, 6.15f,
-R_H_LINE_TO, 8.76f,
-R_CUBIC_TO, -0.38f, 2, -1.53f, 3.7f, -3.25f, 4.83f,
-R_V_LINE_TO, 4,
-R_H_LINE_TO, 5.26f,
-R_CUBIC_TO, 3.08f, -2.77f, 4.85f, -6.87f, 4.85f, -11.75f,
-CLOSE,
-NEW_PATH,
-PATH_COLOR_ARGB, 0xFF, 0x34, 0xA8, 0x53,
-MOVE_TO, 16.27f, 32,
-R_CUBIC_TO, 4.4f, 0, 8.08f, -1.43f, 10.77f, -3.88f,
-R_LINE_TO, -5.26f, -4,
-R_CUBIC_TO, -1.46f, 0.95f, -3.32f, 1.52f, -5.5f, 1.52f,
-R_CUBIC_TO, -4.25f, 0, -7.83f, -2.82f, -9.1f, -6.6f,
-H_LINE_TO, 1.72f,
-R_V_LINE_TO, 4.15f,
-CUBIC_TO, 4.4f, 28.4f, 9.9f, 32, 16.27f, 32,
-CLOSE,
-NEW_PATH,
-PATH_COLOR_ARGB, 0xFF, 0xFB, 0xBC, 0x05,
-MOVE_TO, 7.17f, 19.04f,
-R_CUBIC_TO, -0.33f, -0.96f, -0.5f, -2, -0.5f, -3.04f,
-R_CUBIC_TO, 0, -1.05f, 0.17f, -2.08f, 0.5f, -3.04f,
-V_LINE_TO, 8.8f,
-H_LINE_TO, 1.73f,
-CUBIC_TO, 0.63f, 10.98f, 0, 13.43f, 0, 16,
-R_CUBIC_TO, 0, 2.57f, 0.63f, 5.03f, 1.73f, 7.2f,
-R_LINE_TO, 5.44f, -4.16f,
-CLOSE,
-NEW_PATH,
-PATH_COLOR_ARGB, 0xFF, 0xEA, 0x43, 0x35,
-MOVE_TO, 16.27f, 6.36f,
-R_CUBIC_TO, 2.4f, 0, 4.53f, 0.8f, 6.22f, 2.4f,
-R_LINE_TO, 4.63f, -4.6f,
-CUBIC_TO, 24.33f, 1.6f, 20.63f, 0, 16.23f, 0,
-CUBIC_TO, 9.96f, 0, 4.46f, 3.6f, 1.78f, 8.8f,
-R_LINE_TO, 5.43f, 4.16f,
-R_CUBIC_TO, 1.27f, -3.78f, 4.85f, -6.6f, 9.1f, -6.6f,
-CLOSE
diff --git a/chrome/app/vector_icons/google_chrome/google_pay_logo.icon b/chrome/app/vector_icons/google_chrome/google_pay_logo.icon
deleted file mode 100644
index 03a38032..0000000
--- a/chrome/app/vector_icons/google_chrome/google_pay_logo.icon
+++ /dev/null
@@ -1,116 +0,0 @@
-// Copyright 2018 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-CANVAS_DIMENSIONS, 40,
-
-// "Pay" text.  Color is set by Chrome code at runtime depending on dark mode.
-MOVE_TO, 18.92f, 7.82f,
-R_V_LINE_TO, 4.64f,
-H_LINE_TO, 17.44f,
-V_LINE_TO, 1.01f,
-R_H_LINE_TO, 3.92f,
-R_ARC_TO, 3.55f, 3.55f, 0, 0, 1, 2.54f, 0.99f,
-R_ARC_TO, 3.18f, 3.18f, 0, 0, 1, 1.06f, 2.42f,
-R_ARC_TO, 3.17f, 3.17f, 0, 0, 1, -1.06f, 2.43f,
-R_CUBIC_TO, -0.68f, 0.65f, -1.53f, 0.98f, -2.54f, 0.98f,
-R_H_LINE_TO, -2.44f,
-CLOSE,
-R_MOVE_TO, 0, -5.41f,
-R_V_LINE_TO, 4,
-R_H_LINE_TO, 2.48f,
-R_CUBIC_TO, 0.55f, 0.02f, 1.08f, -0.2f, 1.46f, -0.59f,
-R_ARC_TO, 1.95f, 1.95f, 0, 0, 0, 0, -2.8f,
-R_ARC_TO, 1.93f, 1.93f, 0, 0, 0, -1.46f, -0.61f,
-R_H_LINE_TO, -2.48f,
-CLOSE,
-MOVE_TO, 28.36f, 4.37f,
-R_CUBIC_TO, 1.09f, 0, 1.95f, 0.29f, 2.59f, 0.87f,
-R_CUBIC_TO, 0.63f, 0.58f, 0.95f, 1.38f, 0.95f, 2.39f,
-R_V_LINE_TO, 4.83f,
-R_H_LINE_TO, -1.41f,
-R_V_LINE_TO, -1.09f,
-R_H_LINE_TO, -0.06f,
-R_CUBIC_TO, -0.61f, 0.9f, -1.42f, 1.34f, -2.44f, 1.34f,
-R_CUBIC_TO, -0.87f, 0, -1.59f, -0.26f, -2.18f, -0.77f,
-R_ARC_TO, 2.45f, 2.45f, 0, 0, 1, -0.87f, -1.92f,
-R_CUBIC_TO, 0, -0.81f, 0.31f, -1.46f, 0.92f, -1.93f,
-R_CUBIC_TO, 0.62f, -0.48f, 1.44f, -0.72f, 2.46f, -0.72f,
-R_CUBIC_TO, 0.88f, 0, 1.6f, 0.16f, 2.17f, 0.48f,
-V_LINE_TO, 7.52f,
-R_ARC_TO, 1.67f, 1.67f, 0, 0, 0, -0.61f, -1.3f,
-R_CUBIC_TO, -0.39f, -0.35f, -0.9f, -0.54f, -1.43f, -0.54f,
-R_CUBIC_TO, -0.82f, 0, -1.48f, 0.35f, -1.96f, 1.04f,
-R_LINE_TO, -1.3f, -0.82f,
-R_CUBIC_TO, 0.72f, -1.02f, 1.77f, -1.54f, 3.18f, -1.54f,
-CLOSE,
-R_MOVE_TO, -1.91f, 5.7f,
-R_CUBIC_TO, 0, 0.38f, 0.18f, 0.74f, 0.49f, 0.96f,
-R_CUBIC_TO, 0.33f, 0.26f, 0.73f, 0.39f, 1.15f, 0.38f,
-R_ARC_TO, 2.36f, 2.36f, 0, 0, 0, 1.66f, -0.69f,
-R_CUBIC_TO, 0.49f, -0.46f, 0.73f, -1, 0.73f, -1.62f,
-R_CUBIC_TO, -0.46f, -0.36f, -1.1f, -0.55f, -1.93f, -0.55f,
-R_CUBIC_TO, -0.6f, 0, -1.1f, 0.14f, -1.5f, 0.43f,
-R_CUBIC_TO, -0.4f, 0.29f, -0.61f, 0.65f, -0.61f, 1.08f,
-CLOSE,
-MOVE_TO, 40, 4.62f,
-LINE_TO, 35.07f, 15.92f,
-R_H_LINE_TO, -1.52f,
-R_LINE_TO, 1.83f, -3.95f,
-R_LINE_TO, -3.24f, -7.34f,
-R_H_LINE_TO, 1.61f,
-R_LINE_TO, 2.34f, 5.63f,
-R_H_LINE_TO, 0.03f,
-R_LINE_TO, 2.28f, -5.63f,
-CLOSE,
-
-// Blue part of the G.
-NEW_PATH,
-PATH_COLOR_ARGB, 0xFF, 0x42, 0x85, 0xF4,
-MOVE_TO, 12.95f, 6.82f,
-R_CUBIC_TO, 0, -0.45f, -0.04f, -0.9f, -0.11f, -1.34f,
-R_H_LINE_TO, -6.23f,
-V_LINE_TO, 8.02f,
-R_H_LINE_TO, 3.57f,
-R_ARC_TO, 3.05f, 3.05f, 0, 0, 1, -1.32f, 2,
-R_V_LINE_TO, 1.65f,
-R_H_LINE_TO, 2.13f,
-R_CUBIC_TO, 1.25f, -1.14f, 1.97f, -2.84f, 1.97f, -4.84f,
-CLOSE,
-
-// Green part of the G.
-NEW_PATH,
-PATH_COLOR_ARGB, 0xFF, 0x34, 0xA8, 0x53,
-MOVE_TO, 6.61f, 13.26f,
-R_CUBIC_TO, 1.78f, 0, 3.28f, -0.58f, 4.38f, -1.59f,
-R_LINE_TO, -2.13f, -1.65f,
-R_CUBIC_TO, -0.59f, 0.4f, -1.36f, 0.63f, -2.25f, 0.63f,
-R_CUBIC_TO, -1.72f, 0, -3.19f, -1.16f, -3.71f, -2.72f,
-H_LINE_TO, 0.7f,
-V_LINE_TO, 9.63f,
-R_ARC_TO, 6.61f, 6.61f, 0, 0, 0, 5.9f, 3.63f,
-CLOSE,
-
-// Yellow part of the G.
-NEW_PATH,
-PATH_COLOR_ARGB, 0xFF, 0xFB, 0xBC, 0x05,
-MOVE_TO, 2.9f, 7.93f,
-R_ARC_TO, 3.93f, 3.93f, 0, 0, 1, 0, -2.52f,
-V_LINE_TO, 3.72f,
-H_LINE_TO, 0.7f,
-R_ARC_TO, 6.56f, 6.56f, 0, 0, 0, 0, 5.91f,
-R_LINE_TO, 2.19f, -1.7f,
-CLOSE,
-
-// Red part of the G.
-NEW_PATH,
-PATH_COLOR_ARGB, 0xFF, 0xEA, 0x43, 0x35,
-MOVE_TO, 6.61f, 2.7f,
-R_ARC_TO, 3.59f, 3.59f, 0, 0, 1, 2.53f, 0.99f,
-R_LINE_TO, 1.89f, -1.88f,
-ARC_TO, 6.36f, 6.36f, 0, 0, 0, 6.61f, 0.09f,
-ARC_TO, 6.61f, 6.61f, 0, 0, 0, 0.71f, 3.72f,
-R_LINE_TO, 2.19f, 1.7f,
-R_CUBIC_TO, 0.52f, -1.56f, 1.99f, -2.72f, 3.71f, -2.72f,
-CLOSE
-
diff --git a/chrome/app/vector_icons/google_chrome/product.icon b/chrome/app/vector_icons/google_chrome/product.icon
deleted file mode 100644
index 8f98f16..0000000
--- a/chrome/app/vector_icons/google_chrome/product.icon
+++ /dev/null
@@ -1,42 +0,0 @@
-// Copyright 2015 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-CANVAS_DIMENSIONS, 24,
-// Red
-PATH_COLOR_ARGB, 0xFF, 0xDB, 0x44, 0x37,
-MOVE_TO, 12, 7.5f,
-R_H_LINE_TO, 8.9f,
-CUBIC_TO, 19.3f, 4.2f, 15.9f, 2, 12, 2,
-CUBIC_TO, 8.9f, 2, 6.1f, 3.4f, 4.3f, 5.6f,
-R_LINE_TO, 3.3f, 5.7f,
-R_CUBIC_TO, 0.3f, -2.1f, 2.2f, -3.8f, 4.4f, -3.8f,
-CLOSE,
-NEW_PATH,
-// Green
-PATH_COLOR_ARGB, 0xFF, 0x0F, 0x9D, 0x58,
-// R_MOVE_TO, 0, 9,
-MOVE_TO, 12, 16.5f,
-R_CUBIC_TO, -1.7f, 0, -3.1f, -0.9f, -3.9f, -2.3f,
-LINE_TO, 3.6f, 6.5f,
-CUBIC_TO, 2.6f, 8.1f, 2, 10, 2, 12,
-R_CUBIC_TO, 0, 5, 3.6f, 9.1f, 8.4f, 9.9f,
-R_LINE_TO, 3.3f, -5.7f,
-R_CUBIC_TO, -0.6f, 0.2f, -1.1f, 0.3f, -1.7f, 0.3f,
-CLOSE,
-NEW_PATH,
-// Yellow
-PATH_COLOR_ARGB, 0xFF, 0xFF, 0xCD, 0x40,
-MOVE_TO, 16.5f, 12,
-R_CUBIC_TO, 0, 0.8f, -0.2f, 1.6f, -0.6f, 2.2f,
-LINE_TO, 11.4f, 22,
-R_H_LINE_TO, 0.6f,
-R_CUBIC_TO, 5.5f, 0, 10, -4.5f, 10, -10,
-R_CUBIC_TO, 0, -1.2f, -0.2f, -2.4f, -0.6f, -3.5f,
-R_H_LINE_TO, -6.6f,
-R_CUBIC_TO, 1, 0.8f, 1.7f, 2.1f, 1.7f, 3.5f,
-CLOSE,
-NEW_PATH,
-// Blue
-PATH_COLOR_ARGB, 0xFF, 0x42, 0x85, 0xF4,
-CIRCLE, 12, 12, 3.5
diff --git a/chrome/browser/BUILD.gn b/chrome/browser/BUILD.gn
index 9d15290..3ecd7e9 100644
--- a/chrome/browser/BUILD.gn
+++ b/chrome/browser/BUILD.gn
@@ -1060,8 +1060,6 @@
     "performance_manager/graph/frame_node_impl.h",
     "performance_manager/graph/graph.cc",
     "performance_manager/graph/graph.h",
-    "performance_manager/graph/graph_introspector_impl.cc",
-    "performance_manager/graph/graph_introspector_impl.h",
     "performance_manager/graph/node_attached_data.cc",
     "performance_manager/graph/node_attached_data.h",
     "performance_manager/graph/node_attached_data_impl.h",
@@ -2404,6 +2402,8 @@
       "android/metrics/variations_session.cc",
       "android/mojo/chrome_interface_registrar_android.cc",
       "android/mojo/chrome_interface_registrar_android.h",
+      "android/net/nqe/network_quality_provider.cc",
+      "android/net/nqe/network_quality_provider.h",
       "android/ntp/android_content_suggestions_notifier.cc",
       "android/ntp/android_content_suggestions_notifier.h",
       "android/ntp/content_suggestions_notifier.cc",
@@ -5342,6 +5342,8 @@
       "chromeos/policy/fake_device_cloud_policy_manager.h",
       "chromeos/settings/device_settings_test_helper.cc",
       "chromeos/settings/device_settings_test_helper.h",
+      "ui/ash/ash_test_util.cc",
+      "ui/ash/ash_test_util.h",
       "ui/ash/tablet_mode_client_test_util.cc",
       "ui/ash/tablet_mode_client_test_util.h",
     ]
diff --git a/chrome/browser/about_flags.cc b/chrome/browser/about_flags.cc
index 7aa9dae..b9e8a06e 100644
--- a/chrome/browser/about_flags.cc
+++ b/chrome/browser/about_flags.cc
@@ -2723,7 +2723,8 @@
 
     {"omnibox-ui-show-suggestion-favicons",
      flag_descriptions::kOmniboxUIShowSuggestionFaviconsName,
-     flag_descriptions::kOmniboxUIShowSuggestionFaviconsDescription, kOsDesktop,
+     flag_descriptions::kOmniboxUIShowSuggestionFaviconsDescription,
+     kOsDesktop | kOsAndroid,
      FEATURE_VALUE_TYPE(omnibox::kUIExperimentShowSuggestionFavicons)},
 
     {"omnibox-ui-swap-title-and-url",
@@ -4030,6 +4031,11 @@
      FEATURE_VALUE_TYPE(features::kInSessionPasswordChange)},
 #endif  // OS_CHROMEOS
 
+    {"autofill-off-no-server-data",
+     flag_descriptions::kAutofillOffNoServerDataName,
+     flag_descriptions::kAutofillOffNoServerDataDescription, kOsAll,
+     FEATURE_VALUE_TYPE(autofill::features::kAutofillOffNoServerData)},
+
     {"enable-portals", flag_descriptions::kEnablePortalsName,
      flag_descriptions::kEnablePortalsDescription, kOsAll,
      FEATURE_VALUE_TYPE(blink::features::kPortals)},
diff --git a/chrome/browser/android/chrome_feature_list.cc b/chrome/browser/android/chrome_feature_list.cc
index 77af698c..42264272 100644
--- a/chrome/browser/android/chrome_feature_list.cc
+++ b/chrome/browser/android/chrome_feature_list.cc
@@ -200,6 +200,7 @@
     &omnibox::kOmniboxNewAnswerLayout,
     &omnibox::kOmniboxRichEntitySuggestions,
     &omnibox::kQueryInOmnibox,
+    &omnibox::kUIExperimentShowSuggestionFavicons,
     &password_manager::features::kGooglePasswordManager,
     &password_manager::features::kPasswordsKeyboardAccessory,
     &previews::features::kDataSaverLiteModeRebranding,
diff --git a/chrome/browser/android/net/nqe/network_quality_provider.cc b/chrome/browser/android/net/nqe/network_quality_provider.cc
new file mode 100644
index 0000000..4ceffda
--- /dev/null
+++ b/chrome/browser/android/net/nqe/network_quality_provider.cc
@@ -0,0 +1,65 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/android/net/nqe/network_quality_provider.h"
+
+#include "base/android/jni_android.h"
+#include "chrome/browser/browser_process.h"
+#include "content/public/browser/browser_thread.h"
+#include "content/public/browser/network_quality_observer_factory.h"
+#include "jni/NetworkQualityProvider_jni.h"
+
+using base::android::JavaParamRef;
+using base::android::ScopedJavaLocalRef;
+
+NetworkQualityProvider::NetworkQualityProvider(JNIEnv* env,
+                                               const JavaParamRef<jobject>& obj)
+    : j_obj_(env, obj) {
+  DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
+
+  DCHECK(g_browser_process);
+  g_browser_process->network_quality_tracker()
+      ->AddRTTAndThroughputEstimatesObserver(this);
+  g_browser_process->network_quality_tracker()
+      ->AddEffectiveConnectionTypeObserver(this);
+}
+
+NetworkQualityProvider::~NetworkQualityProvider() {
+  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
+
+  g_browser_process->network_quality_tracker()
+      ->RemoveRTTAndThroughputEstimatesObserver(this);
+  g_browser_process->network_quality_tracker()
+      ->RemoveEffectiveConnectionTypeObserver(this);
+}
+
+void NetworkQualityProvider::OnEffectiveConnectionTypeChanged(
+    net::EffectiveConnectionType type) {
+  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
+
+  JNIEnv* env = base::android::AttachCurrentThread();
+  Java_NetworkQualityProvider_onEffectiveConnectionTypeChanged(
+      env, j_obj_, static_cast<int>(type));
+}
+
+void NetworkQualityProvider::OnRTTOrThroughputEstimatesComputed(
+    base::TimeDelta http_rtt,
+    base::TimeDelta transport_rtt,
+    int32_t downstream_throughput_kbps) {
+  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
+
+  JNIEnv* env = base::android::AttachCurrentThread();
+  Java_NetworkQualityProvider_onRTTOrThroughputEstimatesComputed(
+      env, j_obj_, http_rtt.InMilliseconds(), transport_rtt.InMilliseconds(),
+      downstream_throughput_kbps);
+}
+
+// ----------------------------------------------------------------------------
+// Native JNI methods
+// ----------------------------------------------------------------------------
+
+static jlong JNI_NetworkQualityProvider_Init(JNIEnv* env,
+                                             const JavaParamRef<jobject>& obj) {
+  return reinterpret_cast<intptr_t>(new NetworkQualityProvider(env, obj));
+}
diff --git a/chrome/browser/android/net/nqe/network_quality_provider.h b/chrome/browser/android/net/nqe/network_quality_provider.h
new file mode 100644
index 0000000..e2c781d
--- /dev/null
+++ b/chrome/browser/android/net/nqe/network_quality_provider.h
@@ -0,0 +1,47 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_BROWSER_ANDROID_NET_NQE_NETWORK_QUALITY_PROVIDER_H_
+#define CHROME_BROWSER_ANDROID_NET_NQE_NETWORK_QUALITY_PROVIDER_H_
+
+#include "base/android/scoped_java_ref.h"
+#include "base/macros.h"
+#include "base/threading/thread_checker.h"
+#include "base/time/time.h"
+#include "net/nqe/effective_connection_type.h"
+#include "net/nqe/network_quality.h"
+#include "services/network/public/cpp/network_quality_tracker.h"
+
+// The native instance of the NetworkQualityProvider. This class is not
+// threadsafe and must only be used on the UI thread.
+class NetworkQualityProvider
+    : public network::NetworkQualityTracker::EffectiveConnectionTypeObserver,
+      public network::NetworkQualityTracker::RTTAndThroughputEstimatesObserver {
+ public:
+  NetworkQualityProvider(JNIEnv* env,
+                         const base::android::JavaParamRef<jobject>& obj);
+
+ private:
+  // Note that this destructor is currently dead code. This destructor is never
+  // called as this object is owned by a java singleton that never goes away.
+  ~NetworkQualityProvider() override;
+
+  // net::EffectiveConnectionTypeObserver implementation:
+  void OnEffectiveConnectionTypeChanged(
+      net::EffectiveConnectionType type) override;
+
+  // net::RTTAndThroughputEstimatesObserver implementation:
+  void OnRTTOrThroughputEstimatesComputed(
+      base::TimeDelta http_rtt,
+      base::TimeDelta transport_rtt,
+      int32_t downstream_throughput_kbps) override;
+
+  base::android::ScopedJavaGlobalRef<jobject> j_obj_;
+
+  THREAD_CHECKER(thread_checker_);
+
+  DISALLOW_COPY_AND_ASSIGN(NetworkQualityProvider);
+};
+
+#endif  // CHROME_BROWSER_ANDROID_NET_NQE_NETWORK_QUALITY_PROVIDER_H_
diff --git a/chrome/browser/chrome_browser_main_browsertest.cc b/chrome/browser/chrome_browser_main_browsertest.cc
deleted file mode 100644
index 3fa3a8bf..0000000
--- a/chrome/browser/chrome_browser_main_browsertest.cc
+++ /dev/null
@@ -1,111 +0,0 @@
-// Copyright 2018 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "chrome/browser/chrome_browser_main.h"
-
-#include <stddef.h>
-
-#include "base/command_line.h"
-#include "base/macros.h"
-#include "base/run_loop.h"
-#include "base/test/scoped_feature_list.h"
-#include "chrome/browser/browser_process.h"
-#include "chrome/browser/chrome_browser_main_extra_parts.h"
-#include "chrome/test/base/in_process_browser_test.h"
-#include "components/variations/service/variations_service.h"
-#include "components/variations/variations_switches.h"
-#include "content/public/test/network_connection_change_simulator.h"
-
-// Friend of ChromeBrowserMainPartsTestApi to poke at internal state.
-class ChromeBrowserMainPartsTestApi {
- public:
-  explicit ChromeBrowserMainPartsTestApi(ChromeBrowserMainParts* main_parts)
-      : main_parts_(main_parts) {}
-  ~ChromeBrowserMainPartsTestApi() = default;
-
-  void EnableVariationsServiceInit() {
-    main_parts_
-        ->should_call_pre_main_loop_start_startup_on_variations_service_ = true;
-  }
-
- private:
-  ChromeBrowserMainParts* main_parts_;
-  DISALLOW_COPY_AND_ASSIGN(ChromeBrowserMainPartsTestApi);
-};
-
-namespace {
-
-class ChromeBrowserMainBrowserTest : public InProcessBrowserTest {
- public:
-  ChromeBrowserMainBrowserTest() {
-    net::NetworkChangeNotifier::SetTestNotificationsOnly(true);
-    // Since the test currently performs an actual request to localhost (which
-    // is expected to fail since no variations server is running), retries are
-    // disabled to prevent race conditions from causing flakiness in tests.
-    scoped_feature_list_.InitAndDisableFeature(variations::kHttpRetryFeature);
-  }
-
- protected:
-  // InProcessBrowserTest:
-  void SetUpCommandLine(base::CommandLine* command_line) override {
-    // Without this (and EnableFetchForTesting() below) VariationsService won't
-    // do requests in non-branded builds.
-    command_line->AppendSwitchASCII(variations::switches::kVariationsServerURL,
-                                    "http://localhost");
-  }
-
-  void CreatedBrowserMainParts(
-      content::BrowserMainParts* browser_main_parts) override {
-    variations::VariationsService::EnableFetchForTesting();
-    ChromeBrowserMainParts* chrome_browser_main_parts =
-        static_cast<ChromeBrowserMainParts*>(browser_main_parts);
-    ChromeBrowserMainPartsTestApi(chrome_browser_main_parts)
-        .EnableVariationsServiceInit();
-  }
-
- private:
-  base::test::ScopedFeatureList scoped_feature_list_;
-
-  DISALLOW_COPY_AND_ASSIGN(ChromeBrowserMainBrowserTest);
-};
-
-// Verifies VariationsService does a request when network status changes from
-// none to connected. This is a regression test for https://crbug.com/826930.
-// TODO(crbug.com/905714): This test should use a mock variations server
-// instead of performing an actual request.
-#if defined(OS_CHROMEOS)
-#define MAYBE_VariationsServiceStartsRequestOnNetworkChange \
-  DISABLED_VariationsServiceStartsRequestOnNetworkChange
-#else
-#define MAYBE_VariationsServiceStartsRequestOnNetworkChange \
-  VariationsServiceStartsRequestOnNetworkChange
-#endif
-IN_PROC_BROWSER_TEST_F(ChromeBrowserMainBrowserTest,
-                       MAYBE_VariationsServiceStartsRequestOnNetworkChange) {
-  variations::VariationsService* variations_service =
-      g_browser_process->variations_service();
-  variations_service->CancelCurrentRequestForTesting();
-
-  content::NetworkConnectionChangeSimulator network_change_simulator;
-  network_change_simulator.SetConnectionType(
-      network::mojom::ConnectionType::CONNECTION_NONE);
-  const int initial_request_count = variations_service->request_count();
-
-  // The variations service will only send a request the first time the
-  // connection goes online, or after the 30min delay. Tell it that it hasn't
-  // sent a request yet to make sure the next time we go online a request will
-  // be sent.
-  variations_service->GetResourceRequestAllowedNotifierForTesting()
-      ->SetObserverRequestedForTesting(true);
-
-  network_change_simulator.SetConnectionType(
-      network::mojom::ConnectionType::CONNECTION_WIFI);
-  // NotifyObserversOfNetworkChangeForTests uses PostTask, so run the loop until
-  // idle to ensure VariationsService processes the network change.
-  base::RunLoop().RunUntilIdle();
-  const int final_request_count = variations_service->request_count();
-  EXPECT_EQ(initial_request_count + 1, final_request_count);
-}
-
-}  // namespace
diff --git a/chrome/browser/chromeos/kiosk_next_home/app_controller_service.cc b/chrome/browser/chromeos/kiosk_next_home/app_controller_service.cc
index 617a994..8f995abfe 100644
--- a/chrome/browser/chromeos/kiosk_next_home/app_controller_service.cc
+++ b/chrome/browser/chromeos/kiosk_next_home/app_controller_service.cc
@@ -4,6 +4,7 @@
 
 #include "chrome/browser/chromeos/kiosk_next_home/app_controller_service.h"
 
+#include <sstream>
 #include <string>
 #include <utility>
 #include <vector>
@@ -13,7 +14,6 @@
 #include "base/command_line.h"
 #include "base/logging.h"
 #include "base/optional.h"
-#include "base/strings/string_number_conversions.h"
 #include "base/strings/string_util.h"
 #include "chrome/browser/apps/app_service/app_icon_source.h"
 #include "chrome/browser/apps/app_service/app_service_proxy.h"
@@ -100,9 +100,9 @@
       [](mojom::AppController::GetArcAndroidIdCallback callback, bool success,
          int64_t raw_android_id) {
         // The bridge expects the Android id as a hex string.
-        std::string android_id = base::NumberToString(raw_android_id);
-        std::move(callback).Run(
-            success, base::HexEncode(android_id.data(), android_id.size()));
+        std::stringstream android_id_stream;
+        android_id_stream << std::hex << raw_android_id;
+        std::move(callback).Run(success, android_id_stream.str());
       },
       std::move(callback)));
 }
diff --git a/chrome/browser/chromeos/kiosk_next_home/app_controller_service_unittest.cc b/chrome/browser/chromeos/kiosk_next_home/app_controller_service_unittest.cc
index 8a0d5c9..0a3e4c8 100644
--- a/chrome/browser/chromeos/kiosk_next_home/app_controller_service_unittest.cc
+++ b/chrome/browser/chromeos/kiosk_next_home/app_controller_service_unittest.cc
@@ -4,6 +4,7 @@
 
 #include "chrome/browser/chromeos/kiosk_next_home/app_controller_service.h"
 
+#include <limits>
 #include <map>
 #include <memory>
 #include <utility>
@@ -69,6 +70,12 @@
     arc_test_.app_instance()->SendAppAdded(app_info);
   }
 
+  void SetAndroidId(int64_t android_id) {
+    arc_test_.app_instance()->set_android_id(android_id);
+  }
+
+  void StopArc() { arc_test_.StopArcInstance(); }
+
   void AddAppDeltaToAppService(apps::mojom::AppPtr delta) {
     std::vector<apps::mojom::AppPtr> deltas;
     deltas.push_back(std::move(delta));
@@ -121,6 +128,21 @@
     }
   }
 
+  void ExpectArcAndroidIdResponse(bool success, const std::string& android_id) {
+    bool returned_success;
+    std::string returned_android_id;
+
+    app_controller_service_->GetArcAndroidId(base::BindLambdaForTesting(
+        [&returned_success, &returned_android_id](
+            bool success, const std::string& android_id) {
+          returned_success = success;
+          returned_android_id = android_id;
+        }));
+
+    EXPECT_EQ(returned_success, success);
+    EXPECT_EQ(returned_android_id, android_id);
+  }
+
  private:
   content::TestBrowserThreadBundle test_browser_thread_bundle_;
   std::unique_ptr<TestingProfile> profile_;
@@ -326,5 +348,21 @@
   ExpectApps({allowed_app});
 }
 
+TEST_F(AppControllerServiceTest, GetArcAndroidIdReturnsItWhenItHasIt) {
+  SetAndroidId(123456789L);
+  ExpectArcAndroidIdResponse(true, "75bcd15");  // 75bcd15 is 123456789 in hex.
+
+  // Make sure the returned Android ID doesn't get clipped when it's too large.
+  SetAndroidId(std::numeric_limits<int64_t>::max());
+  ExpectArcAndroidIdResponse(true, "7fffffffffffffff");
+}
+
+TEST_F(AppControllerServiceTest, GetArcAndroidIdFailureIsPropagated) {
+  // Stop the ARC instance to simulate a failure.
+  StopArc();
+
+  ExpectArcAndroidIdResponse(false, "0");
+}
+
 }  // namespace kiosk_next_home
 }  // namespace chromeos
diff --git a/chrome/browser/chromeos/login/enrollment/enrollment_local_policy_server_browsertest.cc b/chrome/browser/chromeos/login/enrollment/enrollment_local_policy_server_browsertest.cc
index 4b02da0b..4e60bf85 100644
--- a/chrome/browser/chromeos/login/enrollment/enrollment_local_policy_server_browsertest.cc
+++ b/chrome/browser/chromeos/login/enrollment/enrollment_local_policy_server_browsertest.cc
@@ -8,6 +8,7 @@
 #include "chrome/browser/chromeos/login/enrollment/auto_enrollment_check_screen.h"
 #include "chrome/browser/chromeos/login/enrollment/enrollment_screen.h"
 #include "chrome/browser/chromeos/login/startup_utils.h"
+#include "chrome/browser/chromeos/login/test/enrollment_ui_mixin.h"
 #include "chrome/browser/chromeos/login/test/fake_gaia_mixin.h"
 #include "chrome/browser/chromeos/login/test/js_checker.h"
 #include "chrome/browser/chromeos/login/test/local_policy_test_server_mixin.h"
@@ -18,12 +19,14 @@
 #include "chrome/browser/chromeos/login/wizard_controller.h"
 #include "chrome/browser/chromeos/policy/browser_policy_connector_chromeos.h"
 #include "chrome/browser/policy/test/local_policy_test_server.h"
+#include "chrome/grit/generated_resources.h"
 #include "chromeos/attestation/mock_attestation_flow.h"
 #include "chromeos/constants/chromeos_switches.h"
 #include "chromeos/cryptohome/async_method_caller.h"
 #include "chromeos/dbus/cryptohome/fake_cryptohome_client.h"
 #include "chromeos/dbus/dbus_thread_manager.h"
 #include "components/policy/core/common/policy_switches.h"
+#include "components/strings/grit/components_strings.h"
 
 namespace chromeos {
 
@@ -54,6 +57,7 @@
     fake_gaia_.SetupFakeGaiaForLogin(FakeGaiaMixin::kFakeUserEmail,
                                      FakeGaiaMixin::kFakeUserGaiaId,
                                      FakeGaiaMixin::kFakeRefreshToken);
+    policy_server_.SetUpdateDeviceAttributesPermission(false);
     OobeBaseTest::SetUpOnMainThread();
   }
 
@@ -81,7 +85,17 @@
     return auto_enrollment_screen;
   }
 
-  LocalPolicyTestServerMixin local_policy_mixin_{&mixin_host_};
+  void TriggerEnrollmentAndSignInSuccessfully() {
+    host()->StartWizard(OobeScreen::SCREEN_OOBE_ENROLLMENT);
+    OobeScreenWaiter(OobeScreen::SCREEN_OOBE_ENROLLMENT).Wait();
+
+    ASSERT_FALSE(StartupUtils::IsDeviceRegistered());
+    enrollment_screen()->OnLoginDone(FakeGaiaMixin::kFakeUserEmail,
+                                     FakeGaiaMixin::kFakeAuthCode);
+  }
+
+  LocalPolicyTestServerMixin policy_server_{&mixin_host_};
+  test::EnrollmentUIMixin enrollment_ui_{&mixin_host_};
   FakeGaiaMixin fake_gaia_{&mixin_host_, embedded_test_server()};
 
  private:
@@ -127,18 +141,190 @@
 
 // Simple manual enrollment.
 IN_PROC_BROWSER_TEST_F(EnrollmentLocalPolicyServerBase, ManualEnrollment) {
-  host()->StartWizard(OobeScreen::SCREEN_OOBE_ENROLLMENT);
-  OobeScreenWaiter(OobeScreen::SCREEN_OOBE_ENROLLMENT).Wait();
+  TriggerEnrollmentAndSignInSuccessfully();
 
-  ASSERT_FALSE(StartupUtils::IsDeviceRegistered());
-  enrollment_screen()->OnLoginDone(FakeGaiaMixin::kFakeUserEmail,
-                                   FakeGaiaMixin::kFakeAuthCode);
-
-  OobeBaseTest::WaitForEnrollmentSuccess();
-  // TODO(rsorokin): Interact with attribute prompt step.
+  enrollment_ui_.WaitForStep(test::ui::kEnrollmentStepSuccess);
   EXPECT_TRUE(StartupUtils::IsDeviceRegistered());
 }
 
+// Simple manual enrollment with device attributes prompt.
+IN_PROC_BROWSER_TEST_F(EnrollmentLocalPolicyServerBase,
+                       ManualEnrollmentWithDeviceAttributes) {
+  policy_server_.SetUpdateDeviceAttributesPermission(true);
+
+  TriggerEnrollmentAndSignInSuccessfully();
+
+  enrollment_ui_.WaitForStep(test::ui::kEnrollmentStepDeviceAttributes);
+  enrollment_ui_.SubmitDeviceAttributes(test::values::kAssetId,
+                                        test::values::kLocation);
+  enrollment_ui_.WaitForStep(test::ui::kEnrollmentStepSuccess);
+  EXPECT_TRUE(StartupUtils::IsDeviceRegistered());
+}
+
+// Simple manual enrollment with only license type available.
+// Client should automatically select the only available license type,
+// so no license selection UI should be displayed.
+IN_PROC_BROWSER_TEST_F(EnrollmentLocalPolicyServerBase,
+                       ManualEnrollmentWithSingleLicense) {
+  policy_server_.ExpectAvailableLicenseCount(5 /* perpetual */, 0 /* annual */,
+                                             0 /* kiosk */);
+
+  TriggerEnrollmentAndSignInSuccessfully();
+
+  enrollment_ui_.WaitForStep(test::ui::kEnrollmentStepSuccess);
+  EXPECT_TRUE(StartupUtils::IsDeviceRegistered());
+}
+
+// Simple manual enrollment with license selection.
+// Enrollment selection UI should be displayed during enrollment.
+IN_PROC_BROWSER_TEST_F(EnrollmentLocalPolicyServerBase,
+                       ManualEnrollmentWithMultipleLicenses) {
+  policy_server_.ExpectAvailableLicenseCount(5 /* perpetual */, 5 /* annual */,
+                                             5 /* kiosk */);
+
+  TriggerEnrollmentAndSignInSuccessfully();
+
+  enrollment_ui_.WaitForStep(test::ui::kEnrollmentStepLicenses);
+  enrollment_ui_.SelectEnrollmentLicense(test::values::kLicenseTypeAnnual);
+  enrollment_ui_.UseSelectedLicense();
+
+  enrollment_ui_.WaitForStep(test::ui::kEnrollmentStepSuccess);
+  EXPECT_TRUE(StartupUtils::IsDeviceRegistered());
+}
+
+// Negative scenarios: see different HTTP error codes in
+// device_management_service.cc
+
+// Error during enrollment : 402 - missing licenses.
+IN_PROC_BROWSER_TEST_F(EnrollmentLocalPolicyServerBase,
+                       EnrollmentErrorNoLicenses) {
+  policy_server_.SetExpectedDeviceEnrollmentError(402);
+
+  TriggerEnrollmentAndSignInSuccessfully();
+
+  enrollment_ui_.WaitForStep(test::ui::kEnrollmentStepError);
+  enrollment_ui_.ExpectErrorMessage(
+      IDS_ENTERPRISE_ENROLLMENT_MISSING_LICENSES_ERROR, /* can retry */ true);
+  enrollment_ui_.RetryAfterError();
+  EXPECT_FALSE(StartupUtils::IsDeviceRegistered());
+}
+
+// Error during enrollment : 403 - management not allowed.
+IN_PROC_BROWSER_TEST_F(EnrollmentLocalPolicyServerBase,
+                       EnrollmentErrorManagementNotAllowed) {
+  policy_server_.SetExpectedDeviceEnrollmentError(403);
+
+  TriggerEnrollmentAndSignInSuccessfully();
+
+  enrollment_ui_.WaitForStep(test::ui::kEnrollmentStepError);
+  enrollment_ui_.ExpectErrorMessage(
+      IDS_ENTERPRISE_ENROLLMENT_AUTH_ACCOUNT_ERROR, /* can retry */ true);
+  enrollment_ui_.RetryAfterError();
+  EXPECT_FALSE(StartupUtils::IsDeviceRegistered());
+}
+
+// Error during enrollment : 405 - invalid device serial.
+IN_PROC_BROWSER_TEST_F(EnrollmentLocalPolicyServerBase,
+                       EnrollmentErrorInvalidDeviceSerial) {
+  policy_server_.SetExpectedDeviceEnrollmentError(405);
+
+  TriggerEnrollmentAndSignInSuccessfully();
+
+  enrollment_ui_.WaitForStep(test::ui::kEnrollmentStepError);
+  // TODO (antrim, rsorokin): find out why it makes sense to retry here?
+  enrollment_ui_.ExpectErrorMessage(
+      IDS_POLICY_DM_STATUS_SERVICE_INVALID_SERIAL_NUMBER,
+      /* can retry */ true);
+  enrollment_ui_.RetryAfterError();
+  EXPECT_FALSE(StartupUtils::IsDeviceRegistered());
+}
+
+// Error during enrollment : 406 - domain mismatch
+IN_PROC_BROWSER_TEST_F(EnrollmentLocalPolicyServerBase,
+                       EnrollmentErrorDomainMismatch) {
+  policy_server_.SetExpectedDeviceEnrollmentError(406);
+
+  TriggerEnrollmentAndSignInSuccessfully();
+
+  enrollment_ui_.WaitForStep(test::ui::kEnrollmentStepError);
+  enrollment_ui_.ExpectErrorMessage(
+      IDS_ENTERPRISE_ENROLLMENT_DOMAIN_MISMATCH_ERROR, /* can retry */ true);
+  enrollment_ui_.RetryAfterError();
+  EXPECT_FALSE(StartupUtils::IsDeviceRegistered());
+}
+
+// Error during enrollment : 409 - Device ID is already in use
+IN_PROC_BROWSER_TEST_F(EnrollmentLocalPolicyServerBase,
+                       EnrollmentErrorDeviceIDConflict) {
+  policy_server_.SetExpectedDeviceEnrollmentError(409);
+
+  TriggerEnrollmentAndSignInSuccessfully();
+
+  enrollment_ui_.WaitForStep(test::ui::kEnrollmentStepError);
+  // TODO (antrim, rsorokin): find out why it makes sense to retry here?
+  enrollment_ui_.ExpectErrorMessage(
+      IDS_POLICY_DM_STATUS_SERVICE_DEVICE_ID_CONFLICT, /* can retry */ true);
+  enrollment_ui_.RetryAfterError();
+  EXPECT_FALSE(StartupUtils::IsDeviceRegistered());
+}
+
+// Error during enrollment : 412 - Activation is pending
+IN_PROC_BROWSER_TEST_F(EnrollmentLocalPolicyServerBase,
+                       EnrollmentErrorActivationIsPending) {
+  policy_server_.SetExpectedDeviceEnrollmentError(412);
+
+  TriggerEnrollmentAndSignInSuccessfully();
+
+  enrollment_ui_.WaitForStep(test::ui::kEnrollmentStepError);
+  enrollment_ui_.ExpectErrorMessage(
+      IDS_POLICY_DM_STATUS_SERVICE_ACTIVATION_PENDING, /* can retry */ true);
+  enrollment_ui_.RetryAfterError();
+  EXPECT_FALSE(StartupUtils::IsDeviceRegistered());
+}
+
+// Error during enrollment : 417 - Consumer account with packaged license.
+IN_PROC_BROWSER_TEST_F(EnrollmentLocalPolicyServerBase,
+                       EnrollmentErrorConsumerAccountWithPackagedLicense) {
+  policy_server_.SetExpectedDeviceEnrollmentError(417);
+
+  TriggerEnrollmentAndSignInSuccessfully();
+
+  enrollment_ui_.WaitForStep(test::ui::kEnrollmentStepError);
+  enrollment_ui_.ExpectErrorMessage(
+      IDS_ENTERPRISE_ENROLLMENT_CONSUMER_ACCOUNT_WITH_PACKAGED_LICENSE,
+      /* can retry */ true);
+  enrollment_ui_.RetryAfterError();
+  EXPECT_FALSE(StartupUtils::IsDeviceRegistered());
+}
+
+// Error during enrollment : 500 - Consumer account with packaged license.
+IN_PROC_BROWSER_TEST_F(EnrollmentLocalPolicyServerBase,
+                       EnrollmentErrorServerError) {
+  policy_server_.SetExpectedDeviceEnrollmentError(500);
+
+  TriggerEnrollmentAndSignInSuccessfully();
+
+  enrollment_ui_.WaitForStep(test::ui::kEnrollmentStepError);
+  enrollment_ui_.ExpectErrorMessage(IDS_POLICY_DM_STATUS_TEMPORARY_UNAVAILABLE,
+                                    /* can retry */ true);
+  enrollment_ui_.RetryAfterError();
+  EXPECT_FALSE(StartupUtils::IsDeviceRegistered());
+}
+
+// Error during enrollment : Strange HTTP response from server.
+IN_PROC_BROWSER_TEST_F(EnrollmentLocalPolicyServerBase,
+                       EnrollmentErrorServerIsDrunk) {
+  policy_server_.SetExpectedDeviceEnrollmentError(12345);
+
+  TriggerEnrollmentAndSignInSuccessfully();
+
+  enrollment_ui_.WaitForStep(test::ui::kEnrollmentStepError);
+  enrollment_ui_.ExpectErrorMessage(IDS_POLICY_DM_STATUS_HTTP_STATUS_ERROR,
+                                    /* can retry */ true);
+  enrollment_ui_.RetryAfterError();
+  EXPECT_FALSE(StartupUtils::IsDeviceRegistered());
+}
+
 // No state keys on the server. Auto enrollment check should proceed to login.
 IN_PROC_BROWSER_TEST_F(AutoEnrollmentLocalPolicyServer, AutoEnrollmentCheck) {
   host()->StartWizard(OobeScreen::SCREEN_AUTO_ENROLLMENT_CHECK);
@@ -147,7 +333,7 @@
 
 // State keys are present but restore mode is not requested.
 IN_PROC_BROWSER_TEST_F(AutoEnrollmentLocalPolicyServer, ReenrollmentNone) {
-  EXPECT_TRUE(local_policy_mixin_.SetDeviceStateRetrievalResponse(
+  EXPECT_TRUE(policy_server_.SetDeviceStateRetrievalResponse(
       state_keys_broker(),
       enterprise_management::DeviceStateRetrievalResponse::RESTORE_MODE_NONE,
       kTestDomain));
@@ -157,7 +343,7 @@
 
 // Reenrollment requested. User can skip.
 IN_PROC_BROWSER_TEST_F(AutoEnrollmentLocalPolicyServer, ReenrollmentRequested) {
-  EXPECT_TRUE(local_policy_mixin_.SetDeviceStateRetrievalResponse(
+  EXPECT_TRUE(policy_server_.SetDeviceStateRetrievalResponse(
       state_keys_broker(),
       enterprise_management::DeviceStateRetrievalResponse::
           RESTORE_MODE_REENROLLMENT_REQUESTED,
@@ -170,7 +356,7 @@
 
 // Reenrollment forced. User can not skip.
 IN_PROC_BROWSER_TEST_F(AutoEnrollmentLocalPolicyServer, ReenrollmentForced) {
-  EXPECT_TRUE(local_policy_mixin_.SetDeviceStateRetrievalResponse(
+  EXPECT_TRUE(policy_server_.SetDeviceStateRetrievalResponse(
       state_keys_broker(),
       enterprise_management::DeviceStateRetrievalResponse::
           RESTORE_MODE_REENROLLMENT_ENFORCED,
@@ -186,7 +372,7 @@
 
 // Device is disabled.
 IN_PROC_BROWSER_TEST_F(AutoEnrollmentLocalPolicyServer, DeviceDisabled) {
-  EXPECT_TRUE(local_policy_mixin_.SetDeviceStateRetrievalResponse(
+  EXPECT_TRUE(policy_server_.SetDeviceStateRetrievalResponse(
       state_keys_broker(),
       enterprise_management::DeviceStateRetrievalResponse::
           RESTORE_MODE_DISABLED,
@@ -198,14 +384,14 @@
 // Attestation enrollment.
 IN_PROC_BROWSER_TEST_F(AutoEnrollmentLocalPolicyServer, Attestation) {
   SetFakeAttestationFlow();
-  EXPECT_TRUE(local_policy_mixin_.SetDeviceStateRetrievalResponse(
+  EXPECT_TRUE(policy_server_.SetDeviceStateRetrievalResponse(
       state_keys_broker(),
       enterprise_management::DeviceStateRetrievalResponse::
           RESTORE_MODE_REENROLLMENT_ZERO_TOUCH,
       kTestDomain));
 
   host()->StartWizard(OobeScreen::SCREEN_AUTO_ENROLLMENT_CHECK);
-  OobeBaseTest::WaitForEnrollmentSuccess();
+  enrollment_ui_.WaitForStep(test::ui::kEnrollmentStepSuccess);
   EXPECT_TRUE(StartupUtils::IsDeviceRegistered());
 }
 
diff --git a/chrome/browser/chromeos/login/enterprise_enrollment_browsertest.cc b/chrome/browser/chromeos/login/enterprise_enrollment_browsertest.cc
index 1d59e886..1b2db68 100644
--- a/chrome/browser/chromeos/login/enterprise_enrollment_browsertest.cc
+++ b/chrome/browser/chromeos/login/enterprise_enrollment_browsertest.cc
@@ -16,6 +16,7 @@
 #include "chrome/browser/chromeos/login/login_manager_test.h"
 #include "chrome/browser/chromeos/login/startup_utils.h"
 #include "chrome/browser/chromeos/login/test/enrollment_helper_mixin.h"
+#include "chrome/browser/chromeos/login/test/enrollment_ui_mixin.h"
 #include "chrome/browser/chromeos/login/test/js_checker.h"
 #include "chrome/browser/chromeos/login/test/oobe_screen_waiter.h"
 #include "chrome/browser/chromeos/login/ui/login_display_host.h"
@@ -147,13 +148,6 @@
     ExecutePendingJavaScript();
   }
 
-  // Fills out the UI with device attribute information and submits it.
-  void SubmitAttributePromptUpdate() {
-    // Fill out the attribute prompt info and submit it.
-    test::OobeJS().TypeIntoPath("asset_id", {"oauth-enroll-asset-id"});
-    test::OobeJS().TypeIntoPath("location", {"oauth-enroll-location"});
-    test::OobeJS().TapOn("enroll-attributes-submit-button");
-  }
 
   // Completes the enrollment process.
   void CompleteEnrollment() {
@@ -170,23 +164,6 @@
   // and the test code.
   void ExecutePendingJavaScript() { test::OobeJS().Evaluate(";"); }
 
-  // Returns true if there are any DOM elements with the given class.
-  bool IsStepDisplayed(const std::string& step) {
-    const std::string js =
-        "document.getElementsByClassName('oauth-enroll-state-" + step +
-        "').length";
-    int count = test::OobeJS().GetInt(js);
-    return count > 0;
-  }
-
-  // Waits until specific enrollment step is displayed.
-  void WaitForStep(const std::string& step) {
-    const std::string js =
-        "document.getElementsByClassName('oauth-enroll-state-" + step +
-        "').length > 0";
-    test::OobeJS().CreateWaiter(js)->Wait();
-  }
-
   // Setup the enrollment screen.
   void ShowEnrollmentScreen() {
     LoginDisplayHost* host = LoginDisplayHost::default_host();
@@ -205,6 +182,7 @@
   }
 
  protected:
+  test::EnrollmentUIMixin enrollment_ui_{&mixin_host_};
   test::EnrollmentHelperMixin enrollment_helper_{&mixin_host_};
 
  private:
@@ -240,7 +218,8 @@
   }
 
   void CheckActiveDirectoryCredentialsShown() {
-    EXPECT_TRUE(IsStepDisplayed("ad-join"));
+    EXPECT_TRUE(
+        enrollment_ui_.IsStepDisplayed(test::ui::kEnrollmentStepADJoin));
     test::OobeJS().ExpectVisiblePath({kAdDialog, kAdCredentialsStep});
     test::OobeJS().ExpectHiddenPath({kAdDialog, kAdUnlockConfigurationStep});
   }
@@ -253,7 +232,8 @@
   }
 
   void CheckActiveDirectoryUnlockConfigurationShown() {
-    EXPECT_TRUE(IsStepDisplayed("ad-join"));
+    EXPECT_TRUE(
+        enrollment_ui_.IsStepDisplayed(test::ui::kEnrollmentStepADJoin));
     test::OobeJS().ExpectHiddenPath({kAdDialog, kAdCredentialsStep});
     test::OobeJS().ExpectVisiblePath({kAdDialog, kAdUnlockConfigurationStep});
   }
@@ -456,8 +436,7 @@
   ExecutePendingJavaScript();
 
   // Verify that the error page is displayed.
-  WaitForStep("error");
-  EXPECT_FALSE(IsStepDisplayed("success"));
+  enrollment_ui_.WaitForStep(test::ui::kEnrollmentStepError);
 }
 
 // Shows the enrollment screen and simulates a successful enrollment. Verifies
@@ -472,8 +451,7 @@
   SubmitEnrollmentCredentials();
 
   // Verify that the success page is displayed.
-  WaitForStep("success");
-  EXPECT_FALSE(IsStepDisplayed("error"));
+  enrollment_ui_.WaitForStep(test::ui::kEnrollmentStepSuccess);
 }
 
 // Shows the enrollment screen and mocks the enrollment helper to request an
@@ -485,19 +463,17 @@
   ShowEnrollmentScreen();
   enrollment_helper_.ExpectEnrollmentMode(
       policy::EnrollmentConfig::MODE_MANUAL);
-  enrollment_helper_.ExpectAttributePromptUpdate("asset_id", "location");
+  enrollment_helper_.ExpectAttributePromptUpdate(test::values::kAssetId,
+                                                 test::values::kLocation);
   enrollment_helper_.ExpectSuccessfulOAuthEnrollment();
   SubmitEnrollmentCredentials();
 
   // Make sure the attribute-prompt view is open.
+  enrollment_ui_.WaitForStep(test::ui::kEnrollmentStepDeviceAttributes);
 
-  WaitForStep("attribute-prompt");
-  EXPECT_TRUE(IsStepDisplayed("attribute-prompt"));
-  EXPECT_FALSE(IsStepDisplayed("success"));
-  EXPECT_FALSE(IsStepDisplayed("error"));
-
-  SubmitAttributePromptUpdate();
-  WaitForStep("success");
+  enrollment_ui_.SubmitDeviceAttributes(test::values::kAssetId,
+                                        test::values::kLocation);
+  enrollment_ui_.WaitForStep(test::ui::kEnrollmentStepSuccess);
 }
 
 // Shows the enrollment screen and mocks the enrollment helper to show license
@@ -517,16 +493,14 @@
   SubmitEnrollmentCredentials();
 
   // Make sure the license selection screen is open.
-  WaitForStep("license");
-  // Click on third option.
-  test::OobeJS().SelectRadioPath(
-      {"oauth-enroll-license-ui", "license-option-kiosk"});
+  enrollment_ui_.WaitForStep(test::ui::kEnrollmentStepLicenses);
+  // Click on Kiosk option.
+  enrollment_ui_.SelectEnrollmentLicense(test::values::kLicenseTypeKiosk);
   // Click on second option. As there is 0 annual licenses, it should not be
   // selected.
-  test::OobeJS().SelectRadioPath(
-      {"oauth-enroll-license-ui", "license-option-annual"});
-  test::OobeJS().TapOnPath({"oauth-enroll-license-ui", "next"});
-  WaitForStep("success");
+  enrollment_ui_.SelectEnrollmentLicense(test::values::kLicenseTypeAnnual);
+  enrollment_ui_.UseSelectedLicense();
+  enrollment_ui_.WaitForStep(test::ui::kEnrollmentStepSuccess);
 }
 
 // Shows the enrollment screen and mocks the enrollment helper to show Active
@@ -553,12 +527,11 @@
   SubmitActiveDirectoryCredentials("machine_name", "" /* machine_dn */, "all",
                                    kAdTestUser, "password");
   WaitForMessage(&message_queue, "\"ShowSpinnerScreen\"");
-  EXPECT_FALSE(IsStepDisplayed("ad-join"));
+  EXPECT_FALSE(enrollment_ui_.IsStepDisplayed(test::ui::kEnrollmentStepADJoin));
 
   CompleteEnrollment();
   // Verify that the success page is displayed.
-  WaitForStep("success");
-  EXPECT_FALSE(IsStepDisplayed("error"));
+  enrollment_ui_.WaitForStep(test::ui::kEnrollmentStepSuccess);
 }
 
 // Verifies that the distinguished name specified on the Active Directory join
@@ -587,12 +560,11 @@
                                    "" /* encryption_types */, kAdTestUser,
                                    "password");
   WaitForMessage(&message_queue, "\"ShowSpinnerScreen\"");
-  EXPECT_FALSE(IsStepDisplayed("ad-join"));
+  EXPECT_FALSE(enrollment_ui_.IsStepDisplayed(test::ui::kEnrollmentStepADJoin));
 
   CompleteEnrollment();
   // Verify that the success page is displayed.
-  WaitForStep("success");
-  EXPECT_FALSE(IsStepDisplayed("error"));
+  enrollment_ui_.WaitForStep(test::ui::kEnrollmentStepSuccess);
 }
 
 // Shows the enrollment screen and mocks the enrollment helper to show Active
@@ -616,7 +588,7 @@
   SubmitActiveDirectoryCredentials("too_long_machine_name", "" /* machine_dn */,
                                    "" /* encryption_types */, kAdTestUser,
                                    "" /* password */);
-  EXPECT_TRUE(IsStepDisplayed("ad-join"));
+  EXPECT_TRUE(enrollment_ui_.IsStepDisplayed(test::ui::kEnrollmentStepADJoin));
   ExpectElementValid(kAdMachineNameInput, true);
   ExpectElementValid(kAdUsernameInput, true);
   ExpectElementValid(kAdPasswordInput, false);
@@ -626,7 +598,7 @@
                                    "" /* encryption_types */, kAdTestUser,
                                    "password");
   WaitForMessage(&message_queue, "\"ShowJoinDomainError\"");
-  EXPECT_TRUE(IsStepDisplayed("ad-join"));
+  EXPECT_TRUE(enrollment_ui_.IsStepDisplayed(test::ui::kEnrollmentStepADJoin));
   ExpectElementValid(kAdMachineNameInput, false);
   ExpectElementValid(kAdUsernameInput, true);
   ExpectElementValid(kAdPasswordInput, true);
@@ -636,7 +608,7 @@
                                    "" /* encryption_types */, "test_user",
                                    "password");
   WaitForMessage(&message_queue, "\"ShowJoinDomainError\"");
-  EXPECT_TRUE(IsStepDisplayed("ad-join"));
+  EXPECT_TRUE(enrollment_ui_.IsStepDisplayed(test::ui::kEnrollmentStepADJoin));
   ExpectElementValid(kAdMachineNameInput, true);
   ExpectElementValid(kAdUsernameInput, false);
   ExpectElementValid(kAdPasswordInput, true);
@@ -659,9 +631,9 @@
   SubmitActiveDirectoryCredentials("machine_name", "" /* machine_dn */,
                                    "legacy", kAdTestUser, "password");
   WaitForMessage(&message_queue, "\"ShowADJoinError\"");
-  WaitForStep("active-directory-join-error");
+  enrollment_ui_.WaitForStep(test::ui::kEnrollmentStepADJoinError);
   test::OobeJS().TapOnPath({kAdErrorCard, kSubmitButton});
-  WaitForStep("ad-join");
+  enrollment_ui_.WaitForStep(test::ui::kEnrollmentStepADJoin);
 }
 
 // Check that configuration for the streamline Active Directory domain join
diff --git a/chrome/browser/chromeos/login/saml/saml_browsertest.cc b/chrome/browser/chromeos/login/saml/saml_browsertest.cc
index 0196cbf..b6ce972 100644
--- a/chrome/browser/chromeos/login/saml/saml_browsertest.cc
+++ b/chrome/browser/chromeos/login/saml/saml_browsertest.cc
@@ -31,6 +31,7 @@
 #include "chrome/browser/chromeos/login/existing_user_controller.h"
 #include "chrome/browser/chromeos/login/screens/gaia_view.h"
 #include "chrome/browser/chromeos/login/startup_utils.h"
+#include "chrome/browser/chromeos/login/test/enrollment_ui_mixin.h"
 #include "chrome/browser/chromeos/login/test/fake_gaia_mixin.h"
 #include "chrome/browser/chromeos/login/test/https_forwarder.h"
 #include "chrome/browser/chromeos/login/test/js_checker.h"
@@ -794,11 +795,13 @@
   guest_view::TestGuestViewManager* GetGuestViewManager();
   content::WebContents* GetEnrollmentContents();
 
- private:
+ protected:
   LocalPolicyTestServerMixin local_policy_mixin_{&mixin_host_};
+  test::EnrollmentUIMixin enrollment_ui_{&mixin_host_};
 
   guest_view::TestGuestViewManagerFactory guest_view_manager_factory_;
 
+ private:
   DISALLOW_COPY_AND_ASSIGN(SAMLEnrollmentTest);
 };
 
@@ -860,7 +863,8 @@
   SigninFrameJS().TypeIntoPath("fake_user", {"Email"});
   SigninFrameJS().TypeIntoPath("fake_password", {"Password"});
   SigninFrameJS().TapOn("Submit");
-  OobeBaseTest::WaitForEnrollmentSuccess();
+
+  enrollment_ui_.WaitForStep(test::ui::kEnrollmentStepDeviceAttributes);
 }
 
 IN_PROC_BROWSER_TEST_F(SAMLEnrollmentTest, WithCredentialsPassingAPI) {
@@ -873,7 +877,7 @@
   SigninFrameJS().TypeIntoPath("fake_password", {"Password"});
   SigninFrameJS().TapOn("Submit");
 
-  OobeBaseTest::WaitForEnrollmentSuccess();
+  enrollment_ui_.WaitForStep(test::ui::kEnrollmentStepDeviceAttributes);
 }
 
 class SAMLPolicyTest : public SamlTest {
diff --git a/chrome/browser/chromeos/login/test/enrollment_ui_mixin.cc b/chrome/browser/chromeos/login/test/enrollment_ui_mixin.cc
new file mode 100644
index 0000000..5f9c6d6
--- /dev/null
+++ b/chrome/browser/chromeos/login/test/enrollment_ui_mixin.cc
@@ -0,0 +1,113 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/chromeos/login/test/enrollment_ui_mixin.h"
+
+#include "chrome/browser/chromeos/login/enrollment/enrollment_screen.h"
+#include "chrome/browser/chromeos/login/test/js_checker.h"
+#include "chrome/browser/chromeos/login/test/test_predicate_waiter.h"
+#include "ui/base/l10n/l10n_util.h"
+
+namespace chromeos {
+namespace test {
+
+namespace ui {
+
+const char kEnrollmentStepSignin[] = "signin";
+const char kEnrollmentStepWorking[] = "working";
+const char kEnrollmentStepSuccess[] = "success";
+const char kEnrollmentStepError[] = "error";
+const char kEnrollmentStepLicenses[] = "license";
+const char kEnrollmentStepDeviceAttributes[] = "attribute-prompt";
+const char kEnrollmentStepADJoin[] = "ad-join";
+const char kEnrollmentStepADJoinError[] = "active-directory-join-error";
+
+}  // namespace ui
+
+namespace values {
+
+const char kLicenseTypePerpetual[] = "perpetual";
+const char kLicenseTypeAnnual[] = "annual";
+const char kLicenseTypeKiosk[] = "kiosk";
+
+const char kAssetId[] = "asset_id";
+const char kLocation[] = "location";
+
+}  // namespace values
+
+namespace {
+
+const char* const kAllSteps[] = {
+    ui::kEnrollmentStepSignin,   ui::kEnrollmentStepWorking,
+    ui::kEnrollmentStepLicenses, ui::kEnrollmentStepDeviceAttributes,
+    ui::kEnrollmentStepSuccess,  ui::kEnrollmentStepADJoin,
+    ui::kEnrollmentStepError,    ui::kEnrollmentStepADJoinError};
+
+std::string StepVisibleExpression(const std::string& step) {
+  return "document.getElementsByClassName('oauth-enroll-state-" + step +
+         "').length > 0";
+}
+
+const std::initializer_list<base::StringPiece> kEnrollmentErrorRetryButtonPath =
+    {"oauth-enroll-error-card", "submitButton"};
+
+}  // namespace
+
+EnrollmentUIMixin::EnrollmentUIMixin(InProcessBrowserTestMixinHost* host)
+    : InProcessBrowserTestMixin(host) {}
+
+EnrollmentUIMixin::~EnrollmentUIMixin() = default;
+
+// Waits until specific enrollment step is displayed.
+void EnrollmentUIMixin::WaitForStep(const std::string& step) {
+  OobeJS().CreateWaiter(StepVisibleExpression(step))->Wait();
+  for (const char* other : kAllSteps) {
+    if (other != step) {
+      ASSERT_FALSE(IsStepDisplayed(other));
+    }
+  }
+}
+// Returns true if there are any DOM elements with the given class.
+bool EnrollmentUIMixin::IsStepDisplayed(const std::string& step) {
+  return OobeJS().GetBool(StepVisibleExpression(step));
+}
+
+void EnrollmentUIMixin::SelectEnrollmentLicense(
+    const std::string& license_type) {
+  OobeJS().SelectRadioPath(
+      {"oauth-enroll-license-ui", "license-option-" + license_type});
+}
+
+void EnrollmentUIMixin::UseSelectedLicense() {
+  OobeJS().TapOnPath({"oauth-enroll-license-ui", "next"});
+}
+
+void EnrollmentUIMixin::ExpectErrorMessage(int error_message_id,
+                                           bool can_retry) {
+  const std::string element_path =
+      GetOobeElementPath({"oauth-enroll-error-card"});
+  const std::string message = OobeJS().GetString(element_path + ".textContent");
+  ASSERT_TRUE(std::string::npos !=
+              message.find(l10n_util::GetStringUTF8(error_message_id)));
+  if (can_retry) {
+    OobeJS().ExpectVisiblePath(kEnrollmentErrorRetryButtonPath);
+  } else {
+    OobeJS().ExpectHiddenPath(kEnrollmentErrorRetryButtonPath);
+  }
+}
+
+void EnrollmentUIMixin::RetryAfterError() {
+  OobeJS().TapOnPath(kEnrollmentErrorRetryButtonPath);
+  WaitForStep(ui::kEnrollmentStepSignin);
+}
+
+void EnrollmentUIMixin::SubmitDeviceAttributes(const std::string& asset_id,
+                                               const std::string& location) {
+  OobeJS().TypeIntoPath(asset_id, {"oauth-enroll-asset-id"});
+  OobeJS().TypeIntoPath(location, {"oauth-enroll-location"});
+  OobeJS().TapOn("enroll-attributes-submit-button");
+}
+
+}  // namespace test
+}  // namespace chromeos
diff --git a/chrome/browser/chromeos/login/test/enrollment_ui_mixin.h b/chrome/browser/chromeos/login/test/enrollment_ui_mixin.h
new file mode 100644
index 0000000..37407de
--- /dev/null
+++ b/chrome/browser/chromeos/login/test/enrollment_ui_mixin.h
@@ -0,0 +1,74 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_BROWSER_CHROMEOS_LOGIN_TEST_ENROLLMENT_UI_MIXIN_H_
+#define CHROME_BROWSER_CHROMEOS_LOGIN_TEST_ENROLLMENT_UI_MIXIN_H_
+
+#include <string>
+
+#include "base/macros.h"
+#include "chrome/browser/chromeos/login/mixin_based_in_process_browser_test.h"
+
+namespace chromeos {
+namespace test {
+
+namespace ui {
+
+//  WaitForStep(...) constants.
+
+extern const char kEnrollmentStepLicenses[];
+extern const char kEnrollmentStepDeviceAttributes[];
+extern const char kEnrollmentStepSuccess[];
+extern const char kEnrollmentStepADJoin[];
+extern const char kEnrollmentStepError[];
+extern const char kEnrollmentStepADJoinError[];
+
+}  // namespace ui
+
+namespace values {
+
+// SelectEnrollmentLicense(...) constants.
+
+extern const char kLicenseTypePerpetual[];
+extern const char kLicenseTypeKiosk[];
+extern const char kLicenseTypeAnnual[];
+
+// SubmitDeviceAttributes common values.
+
+extern const char kAssetId[];
+extern const char kLocation[];
+
+}  // namespace values
+
+// This test mixin covers enrollment-specific OOBE UI interactions.
+class EnrollmentUIMixin : public InProcessBrowserTestMixin {
+ public:
+  explicit EnrollmentUIMixin(InProcessBrowserTestMixinHost* host);
+  ~EnrollmentUIMixin() override;
+
+  // Waits until specific enrollment step is displayed.
+  void WaitForStep(const std::string& step);
+  bool IsStepDisplayed(const std::string& step);
+
+  void ExpectErrorMessage(int error_message_id, bool can_retry);
+  void RetryAfterError();
+
+  // Fills out the UI with device attribute information and submits it.
+  void SubmitDeviceAttributes(const std::string& asset_id,
+                              const std::string& location);
+
+  // Selects enrollment license.
+  void SelectEnrollmentLicense(const std::string& license_type);
+
+  // Proceeds with selected license.
+  void UseSelectedLicense();
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(EnrollmentUIMixin);
+};
+
+}  // namespace test
+}  // namespace chromeos
+
+#endif  // CHROME_BROWSER_CHROMEOS_LOGIN_TEST_ENROLLMENT_UI_MIXIN_H_
diff --git a/chrome/browser/chromeos/login/test/local_policy_test_server_mixin.cc b/chrome/browser/chromeos/login/test/local_policy_test_server_mixin.cc
index 481199be..c7b7b08 100644
--- a/chrome/browser/chromeos/login/test/local_policy_test_server_mixin.cc
+++ b/chrome/browser/chromeos/login/test/local_policy_test_server_mixin.cc
@@ -58,6 +58,38 @@
                                   policy_test_server_->GetServiceURL().spec());
 }
 
+void LocalPolicyTestServerMixin::ExpectAvailableLicenseCount(int perpetual,
+                                                             int annual,
+                                                             int kiosk) {
+  base::Value licenses(base::Value::Type::DICTIONARY);
+  if (perpetual >= 0) {
+    licenses.SetKey("perpetual", base::Value(perpetual));
+  }
+  if (annual >= 0) {
+    licenses.SetKey("annual", base::Value(annual));
+  }
+  if (kiosk >= 0) {
+    licenses.SetKey("kiosk", base::Value(kiosk));
+  }
+  DCHECK(licenses.DictSize() > 0);
+
+  server_config_.SetKey("available_licenses", std::move(licenses));
+  policy_test_server_->SetConfig(server_config_);
+}
+
+void LocalPolicyTestServerMixin::SetUpdateDeviceAttributesPermission(
+    bool allowed) {
+  server_config_.SetKey("allow_set_device_attributes", base::Value(allowed));
+  policy_test_server_->SetConfig(server_config_);
+}
+
+void LocalPolicyTestServerMixin::SetExpectedDeviceEnrollmentError(
+    int net_error_code) {
+  server_config_.SetKey("device_register_http_error",
+                        base::Value(net_error_code));
+  policy_test_server_->SetConfig(server_config_);
+}
+
 bool LocalPolicyTestServerMixin::UpdateDevicePolicy(
     const enterprise_management::ChromeDeviceSettingsProto& policy) {
   DCHECK(policy_test_server_);
diff --git a/chrome/browser/chromeos/login/test/local_policy_test_server_mixin.h b/chrome/browser/chromeos/login/test/local_policy_test_server_mixin.h
index cc65c82c..7db656f 100644
--- a/chrome/browser/chromeos/login/test/local_policy_test_server_mixin.h
+++ b/chrome/browser/chromeos/login/test/local_policy_test_server_mixin.h
@@ -34,6 +34,18 @@
   bool UpdateDevicePolicy(
       const enterprise_management::ChromeDeviceSettingsProto& policy);
 
+  // Configures and sets expectations for enrollment flow with license
+  // selection. Non-negative values indicate number of available licenses.
+  // There should be at least one license type.
+  void ExpectAvailableLicenseCount(int perpetual, int annual, int kiosk);
+
+  void SetUpdateDeviceAttributesPermission(bool allowed);
+
+  // Configures server to respond with particular error code during device
+  // registration.
+  // |net_error_code| - error code from device_management_service.cc.
+  void SetExpectedDeviceEnrollmentError(int net_error_code);
+
   // Set response for DeviceStateRetrievalRequest. Returns that if finds state
   // key passed in the request. State keys could be set by RegisterClient call
   // on policy test server.
diff --git a/chrome/browser/chromeos/login/test/oobe_base_test.cc b/chrome/browser/chromeos/login/test/oobe_base_test.cc
index 29e90ab8..74d4484 100644
--- a/chrome/browser/chromeos/login/test/oobe_base_test.cc
+++ b/chrome/browser/chromeos/login/test/oobe_base_test.cc
@@ -173,14 +173,6 @@
   } while (message != "\"Done\"");
 }
 
-void OobeBaseTest::WaitForEnrollmentSuccess() {
-  test::OobeJS()
-      .CreateWaiter(
-          "document.getElementsByClassName('oauth-enroll-state-attribute-"
-          "prompt').length > 0")
-      ->Wait();
-}
-
 void OobeBaseTest::WaitForSigninScreen() {
   WizardController* wizard_controller = WizardController::default_controller();
   if (wizard_controller)
diff --git a/chrome/browser/conflicts/module_inspector_win.cc b/chrome/browser/conflicts/module_inspector_win.cc
index b8077f6..969efba 100644
--- a/chrome/browser/conflicts/module_inspector_win.cc
+++ b/chrome/browser/conflicts/module_inspector_win.cc
@@ -39,14 +39,6 @@
   base::UmaHistogramBoolean("Windows.InspectModule.ConnectionError", value);
 }
 
-base::FilePath GetInspectionResultsCachePath() {
-  base::FilePath user_data_dir;
-  if (!base::PathService::Get(chrome::DIR_USER_DATA, &user_data_dir))
-    return base::FilePath();
-
-  return user_data_dir.Append(L"Module Info Cache");
-}
-
 // Reads the inspection results cache and records the result in UMA.
 InspectionResultsCache ReadInspectionResultsCacheOnBackgroundSequence(
     const base::FilePath& file_path) {
@@ -80,6 +72,9 @@
 // static
 constexpr base::Feature ModuleInspector::kWinOOPInspectModuleFeature;
 
+// static
+constexpr base::TimeDelta ModuleInspector::kFlushInspectionResultsTimerTimeout;
+
 ModuleInspector::ModuleInspector(
     const OnModuleInspectedCallback& on_module_inspected_callback)
     : on_module_inspected_callback_(on_module_inspected_callback),
@@ -90,8 +85,15 @@
       path_mapping_(GetPathMapping()),
       cache_task_runner_(base::CreateSequencedTaskRunnerWithTraits(
           {base::MayBlock(), base::TaskPriority::BEST_EFFORT,
-           base::TaskShutdownBehavior::SKIP_ON_SHUTDOWN})),
+           base::TaskShutdownBehavior::BLOCK_SHUTDOWN})),
       inspection_results_cache_read_(false),
+      flush_inspection_results_timer_(
+          FROM_HERE,
+          kFlushInspectionResultsTimerTimeout,
+          base::BindRepeating(
+              &ModuleInspector::MaybeUpdateInspectionResultsCache,
+              base::Unretained(this))),
+      has_new_inspection_results_(false),
       connection_error_retry_count_(kConnectionErrorRetryCount),
       background_inspection_disabled_(
           base::FeatureList::IsEnabled(kDisableBackgroundModuleInspection)),
@@ -144,11 +146,23 @@
 }
 
 void ModuleInspector::OnModuleDatabaseIdle() {
-  // Serialize cache.
-  cache_task_runner_->PostTask(
-      FROM_HERE, base::BindOnce(&WriteInspectionResultCacheOnBackgroundSequence,
-                                GetInspectionResultsCachePath(),
-                                inspection_results_cache_));
+  MaybeUpdateInspectionResultsCache();
+}
+
+// static
+base::FilePath ModuleInspector::GetInspectionResultsCachePath() {
+  base::FilePath user_data_dir;
+  if (!base::PathService::Get(chrome::DIR_USER_DATA, &user_data_dir))
+    return base::FilePath();
+
+  return user_data_dir.Append(L"Module Info Cache");
+}
+
+void ModuleInspector::SetModuleInspectionResultForTesting(
+    const ModuleInfoKey& module_key,
+    ModuleInspectionResult inspection_result) {
+  AddInspectionResultToCache(module_key, inspection_result,
+                             &inspection_results_cache_);
 }
 
 void ModuleInspector::EnsureUtilWinServiceBound() {
@@ -285,6 +299,9 @@
   // easily comparable.
   CollapseMatchingPrefixInPath(path_mapping_, &inspection_result.location);
 
+  has_new_inspection_results_ = true;
+  if (!flush_inspection_results_timer_.IsRunning())
+    flush_inspection_results_timer_.Reset();
   AddInspectionResultToCache(module_key, inspection_result,
                              &inspection_results_cache_);
 
@@ -313,3 +330,16 @@
   if (!queue_.empty())
     StartInspectingModule();
 }
+
+void ModuleInspector::MaybeUpdateInspectionResultsCache() {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+
+  if (!has_new_inspection_results_)
+    return;
+
+  has_new_inspection_results_ = false;
+  cache_task_runner_->PostTask(
+      FROM_HERE, base::BindOnce(&WriteInspectionResultCacheOnBackgroundSequence,
+                                GetInspectionResultsCachePath(),
+                                inspection_results_cache_));
+}
diff --git a/chrome/browser/conflicts/module_inspector_win.h b/chrome/browser/conflicts/module_inspector_win.h
index 81a6e38..5a414f5f 100644
--- a/chrome/browser/conflicts/module_inspector_win.h
+++ b/chrome/browser/conflicts/module_inspector_win.h
@@ -15,6 +15,7 @@
 #include "base/memory/weak_ptr.h"
 #include "base/sequence_checker.h"
 #include "base/task/task_traits.h"
+#include "base/timer/timer.h"
 #include "chrome/browser/conflicts/inspection_results_cache_win.h"
 #include "chrome/browser/conflicts/module_database_observer_win.h"
 #include "chrome/browser/conflicts/module_info_win.h"
@@ -51,6 +52,11 @@
   static constexpr base::Feature kWinOOPInspectModuleFeature = {
       "WinOOPInspectModule", base::FEATURE_DISABLED_BY_DEFAULT};
 
+  // The amount of time before the |inspection_results_cache_| is flushed to
+  // disk while the ModuleDatabase is not idle.
+  static constexpr base::TimeDelta kFlushInspectionResultsTimerTimeout =
+      base::TimeDelta::FromMinutes(5);
+
   using OnModuleInspectedCallback =
       base::Callback<void(const ModuleInfoKey& module_key,
                           ModuleInspectionResult inspection_result)>;
@@ -76,6 +82,12 @@
     test_connector_ = connector;
   }
 
+  static base::FilePath GetInspectionResultsCachePath();
+
+  void SetModuleInspectionResultForTesting(
+      const ModuleInfoKey& module_key,
+      ModuleInspectionResult inspection_result);
+
  private:
   // Ensures the |util_win_ptr_| instance is bound to the UtilWin service. This
   // may result in an unbounded pointer if Chrome is currently shutting down.
@@ -107,6 +119,10 @@
   void OnInspectionFinished(const ModuleInfoKey& module_key,
                             ModuleInspectionResult inspection_result);
 
+  // Sends a task on a blocking background sequence to serialize
+  // |inspection_results_cache_|, should it be needed.
+  void MaybeUpdateInspectionResultsCache();
+
   OnModuleInspectedCallback on_module_inspected_callback_;
 
   // The modules are put in queue until they are sent for inspection.
@@ -141,6 +157,14 @@
   // more than once between restarts.
   InspectionResultsCache inspection_results_cache_;
 
+  // Ensures that newly inspected modules are flushed to the disk after at most
+  // 5 minutes to avoid losing too much of the work done if the browser is
+  // closed before all modules are inspected.
+  base::RetainingOneShotTimer flush_inspection_results_timer_;
+
+  // Indicates if a module was newly inspected and the cache must be updated.
+  bool has_new_inspection_results_;
+
   // The number of time this class will try to restart the UtilWin service if a
   // connection error occurs. This is to prevent the degenerate case where the
   // service always fails to start and the restart cycle happens infinitely.
diff --git a/chrome/browser/conflicts/module_inspector_win_unittest.cc b/chrome/browser/conflicts/module_inspector_win_unittest.cc
index c5e76bb..a6a402d 100644
--- a/chrome/browser/conflicts/module_inspector_win_unittest.cc
+++ b/chrome/browser/conflicts/module_inspector_win_unittest.cc
@@ -12,8 +12,11 @@
 #include "base/bind.h"
 #include "base/environment.h"
 #include "base/files/file_path.h"
+#include "base/files/scoped_temp_dir.h"
 #include "base/macros.h"
 #include "base/test/scoped_feature_list.h"
+#include "base/test/scoped_path_override.h"
+#include "chrome/common/chrome_paths.h"
 #include "chrome/services/util_win/public/mojom/constants.mojom.h"
 #include "chrome/services/util_win/util_win_service.h"
 #include "content/public/test/test_browser_thread_bundle.h"
@@ -33,9 +36,25 @@
   return path;
 }
 
+bool CreateInspectionResultsCacheWithEntry(
+    const ModuleInfoKey& module_key,
+    const ModuleInspectionResult& inspection_result) {
+  // First create a cache with bogus data and create the cache file.
+  InspectionResultsCache inspection_results_cache;
+
+  AddInspectionResultToCache(module_key, inspection_result,
+                             &inspection_results_cache);
+
+  return WriteInspectionResultsCache(
+      ModuleInspector::GetInspectionResultsCachePath(),
+      inspection_results_cache);
+}
+
 class ModuleInspectorTest : public testing::Test {
  public:
-  ModuleInspectorTest() = default;
+  ModuleInspectorTest()
+      : test_browser_thread_bundle_(
+            base::test::ScopedTaskEnvironment::MainThreadType::MOCK_TIME) {}
 
   // Callback for ModuleInspector.
   void OnModuleInspected(const ModuleInfoKey& module_key,
@@ -44,11 +63,18 @@
   }
 
   void RunUntilIdle() { test_browser_thread_bundle_.RunUntilIdle(); }
+  void FastForwardToIdleTimer() {
+    test_browser_thread_bundle_.FastForwardBy(
+        ModuleInspector::kFlushInspectionResultsTimerTimeout);
+    test_browser_thread_bundle_.RunUntilIdle();
+  }
 
   const std::vector<ModuleInspectionResult>& inspected_modules() {
     return inspected_modules_;
   }
 
+  void ClearInspectedModules() { inspected_modules_.clear(); }
+
   // A TestBrowserThreadBundle is required instead of a ScopedTaskEnvironment
   // because of AfterStartupTaskUtils (DCHECK for BrowserThread::UI).
   //
@@ -146,3 +172,114 @@
   RunUntilIdle();
   EXPECT_EQ(2u, inspected_modules().size());
 }
+
+TEST_F(ModuleInspectorTest, InspectionResultsCache) {
+  base::test::ScopedFeatureList scoped_feature_list;
+  scoped_feature_list.InitAndEnableFeature(kInspectionResultsCache);
+
+  base::ScopedTempDir scoped_temp_dir;
+  ASSERT_TRUE(scoped_temp_dir.CreateUniqueTempDir());
+
+  base::ScopedPathOverride scoped_user_data_dir_override(
+      chrome::DIR_USER_DATA, scoped_temp_dir.GetPath());
+
+  // First create a cache with bogus data and create the cache file.
+  ModuleInfoKey module_key(GetKernel32DllFilePath(), 0, 0);
+  ModuleInspectionResult inspection_result;
+  inspection_result.location = L"BogusLocation";
+  inspection_result.basename = L"BogusBasename";
+
+  ASSERT_TRUE(
+      CreateInspectionResultsCacheWithEntry(module_key, inspection_result));
+
+  ModuleInspector module_inspector(base::Bind(
+      &ModuleInspectorTest::OnModuleInspected, base::Unretained(this)));
+
+  module_inspector.AddModule(module_key);
+
+  RunUntilIdle();
+
+  ASSERT_EQ(1u, inspected_modules().size());
+
+  // The following comparisons can only succeed if the module was truly read
+  // from the cache.
+  ASSERT_EQ(inspected_modules()[0].location, inspection_result.location);
+  ASSERT_EQ(inspected_modules()[0].basename, inspection_result.basename);
+}
+
+// Tests that when OnModuleDatabaseIdle() notificate is received, the cache is
+// flushed to disk.
+TEST_F(ModuleInspectorTest, InspectionResultsCache_OnModuleDatabaseIdle) {
+  base::test::ScopedFeatureList scoped_feature_list;
+  scoped_feature_list.InitAndEnableFeature(kInspectionResultsCache);
+
+  base::ScopedTempDir scoped_temp_dir;
+  ASSERT_TRUE(scoped_temp_dir.CreateUniqueTempDir());
+
+  base::ScopedPathOverride scoped_user_data_dir_override(
+      chrome::DIR_USER_DATA, scoped_temp_dir.GetPath());
+
+  ModuleInspector module_inspector(base::Bind(
+      &ModuleInspectorTest::OnModuleInspected, base::Unretained(this)));
+
+  ModuleInfoKey module_key(GetKernel32DllFilePath(), 0, 0);
+  module_inspector.AddModule(module_key);
+
+  RunUntilIdle();
+
+  ASSERT_EQ(1u, inspected_modules().size());
+
+  module_inspector.OnModuleDatabaseIdle();
+  RunUntilIdle();
+
+  // If the cache was written to disk, it should contain the one entry for
+  // Kernel32.dll.
+  InspectionResultsCache inspection_results_cache;
+  EXPECT_EQ(ReadInspectionResultsCache(
+                ModuleInspector::GetInspectionResultsCachePath(), 0,
+                &inspection_results_cache),
+            ReadCacheResult::kSuccess);
+
+  EXPECT_EQ(inspection_results_cache.size(), 1u);
+  auto inspection_result =
+      GetInspectionResultFromCache(module_key, &inspection_results_cache);
+  EXPECT_TRUE(inspection_result);
+}
+
+// Tests that when the timer expires before the OnModuleDatabaseIdle()
+// notification, the cache is flushed to disk.
+TEST_F(ModuleInspectorTest, InspectionResultsCache_TimerExpired) {
+  base::test::ScopedFeatureList scoped_feature_list;
+  scoped_feature_list.InitAndEnableFeature(kInspectionResultsCache);
+
+  base::ScopedTempDir scoped_temp_dir;
+  ASSERT_TRUE(scoped_temp_dir.CreateUniqueTempDir());
+
+  base::ScopedPathOverride scoped_user_data_dir_override(
+      chrome::DIR_USER_DATA, scoped_temp_dir.GetPath());
+
+  ModuleInspector module_inspector(base::Bind(
+      &ModuleInspectorTest::OnModuleInspected, base::Unretained(this)));
+
+  ModuleInfoKey module_key(GetKernel32DllFilePath(), 0, 0);
+  module_inspector.AddModule(module_key);
+
+  RunUntilIdle();
+
+  ASSERT_EQ(1u, inspected_modules().size());
+
+  // Fast forwarding until the timer is fired.
+  FastForwardToIdleTimer();
+
+  // If the cache was flushed, it should contain the one entry for Kernel32.dll.
+  InspectionResultsCache inspection_results_cache;
+  EXPECT_EQ(ReadInspectionResultsCache(
+                ModuleInspector::GetInspectionResultsCachePath(), 0,
+                &inspection_results_cache),
+            ReadCacheResult::kSuccess);
+
+  EXPECT_EQ(inspection_results_cache.size(), 1u);
+  auto inspection_result =
+      GetInspectionResultFromCache(module_key, &inspection_results_cache);
+  EXPECT_TRUE(inspection_result);
+}
diff --git a/chrome/browser/extensions/extension_bindings_apitest.cc b/chrome/browser/extensions/extension_bindings_apitest.cc
index 26e77d4..1d7a9f06 100644
--- a/chrome/browser/extensions/extension_bindings_apitest.cc
+++ b/chrome/browser/extensions/extension_bindings_apitest.cc
@@ -915,6 +915,57 @@
   EXPECT_EQ("success", result);
 }
 
+// Tests the aliasing of chrome.extension methods to their chrome.runtime
+// equivalents.
+IN_PROC_BROWSER_TEST_F(ExtensionBindingsApiTest,
+                       ChromeExtensionIsAliasedToChromeRuntime) {
+  constexpr char kManifest[] =
+      R"({
+           "name": "Test",
+           "version": "0.1",
+           "manifest_version": 2,
+           "background": { "scripts": ["background.js"] }
+         })";
+  constexpr char kBackground[] =
+      R"(chrome.test.runTests([
+           function chromeExtensionIsAliased() {
+             // Sanity check: chrome.extension is directly aliased to
+             // chrome.runtime.
+             chrome.test.assertTrue(!!chrome.runtime);
+             chrome.test.assertTrue(!!chrome.runtime.sendMessage);
+             chrome.test.assertEq(chrome.runtime.sendMessage,
+                                  chrome.extension.sendMessage);
+             chrome.test.succeed();
+           },
+           function testOverridingFailsGracefully() {
+             let intercepted = false;
+             // Modify the chrome.runtime object, which is the source for the
+             // chrome.extension API, to throw an error when sendMessage is
+             // accessed. Nothing should blow up.
+             // Regression test for https://crbug.com/949170.
+             Object.defineProperty(
+                 chrome.runtime,
+                 'sendMessage',
+                 {
+                   get() {
+                     intercepted = true;
+                     throw new Error('Mwahaha');
+                   }
+                 });
+             chrome.extension.sendMessage;
+             chrome.test.assertTrue(intercepted);
+             chrome.test.succeed();
+           }
+         ]);)";
+
+  TestExtensionDir extension_dir;
+  extension_dir.WriteManifest(kManifest);
+  extension_dir.WriteFile(FILE_PATH_LITERAL("background.js"), kBackground);
+  ResultCatcher catcher;
+  ASSERT_TRUE(LoadExtension(extension_dir.UnpackedPath()));
+  EXPECT_TRUE(catcher.GetNextResult()) << catcher.message();
+}
+
 INSTANTIATE_TEST_SUITE_P(,
                          ExtensionBindingsUserGestureTest,
                          ::testing::Values(kUserActivationV1,
diff --git a/chrome/browser/flag-metadata.json b/chrome/browser/flag-metadata.json
index c04bfc8..4f212af1 100644
--- a/chrome/browser/flag-metadata.json
+++ b/chrome/browser/flag-metadata.json
@@ -918,6 +918,11 @@
     "expiry_milestone": 77
   },
   {
+    "name": "autofill-off-no-server-data",
+    "owners": [ "seblalancette" ],
+    "expiry_milestone": 79
+  },
+  {
     "name": "enable-autoplay-ignore-web-audio",
     "owners": [ "beccahughes", "mlamouri", "media-dev" ],
     "expiry_milestone": 73
diff --git a/chrome/browser/flag_descriptions.cc b/chrome/browser/flag_descriptions.cc
index 41771ef4..073657a 100644
--- a/chrome/browser/flag_descriptions.cc
+++ b/chrome/browser/flag_descriptions.cc
@@ -139,6 +139,11 @@
     "When enabled, no local copy of server card will be saved when credit card "
     "upload succeeds.";
 
+const char kAutofillOffNoServerDataName[] = "Autofill Off No Server Data";
+const char kAutofillOffNoServerDataDescription[] =
+    "Disables Autofill for fields with autocomplete off that have no "
+    "crowd-sourced evidence that Autofill would be helpful.";
+
 const char kAutofillProfileClientValidationName[] =
     "Autofill Validates Profiles By Client";
 const char kAutofillProfileClientValidationDescription[] =
diff --git a/chrome/browser/flag_descriptions.h b/chrome/browser/flag_descriptions.h
index 10dde3a..b7c07d4 100644
--- a/chrome/browser/flag_descriptions.h
+++ b/chrome/browser/flag_descriptions.h
@@ -113,6 +113,9 @@
 extern const char kAutofillNoLocalSaveOnUploadSuccessName[];
 extern const char kAutofillNoLocalSaveOnUploadSuccessDescription[];
 
+extern const char kAutofillOffNoServerDataName[];
+extern const char kAutofillOffNoServerDataDescription[];
+
 extern const char kAutofillProfileClientValidationName[];
 extern const char kAutofillProfileClientValidationDescription[];
 
diff --git a/chrome/browser/metrics/process_memory_metrics_emitter.cc b/chrome/browser/metrics/process_memory_metrics_emitter.cc
index 809942b..f055953 100644
--- a/chrome/browser/metrics/process_memory_metrics_emitter.cc
+++ b/chrome/browser/metrics/process_memory_metrics_emitter.cc
@@ -4,6 +4,8 @@
 
 #include "chrome/browser/metrics/process_memory_metrics_emitter.h"
 
+#include <set>
+
 #include "base/bind.h"
 #include "base/compiler_specific.h"
 #include "base/metrics/histogram_functions.h"
@@ -12,6 +14,10 @@
 #include "build/build_config.h"
 #include "chrome/browser/browser_process.h"
 #include "chrome/browser/metrics/tab_footprint_aggregator.h"
+#include "chrome/browser/performance_manager/graph/frame_node_impl.h"
+#include "chrome/browser/performance_manager/graph/graph.h"
+#include "chrome/browser/performance_manager/graph/page_node_impl.h"
+#include "chrome/browser/performance_manager/graph/process_node_impl.h"
 #include "chrome/browser/performance_manager/performance_manager.h"
 #include "chrome/browser/profiles/profile_manager.h"
 #include "content/public/browser/audio_service_info.h"
@@ -398,14 +404,13 @@
 
 void EmitRendererMemoryMetrics(
     const GlobalMemoryDump::ProcessDump& pmd,
-    const resource_coordinator::mojom::PageInfoPtr& page_info,
+    const ProcessMemoryMetricsEmitter::PageInfo* page_info,
     ukm::UkmRecorder* ukm_recorder,
     int number_of_extensions,
     const base::Optional<base::TimeDelta>& uptime,
     bool record_uma) {
-  ukm::SourceId ukm_source_id = page_info.is_null()
-                                    ? ukm::UkmRecorder::GetNewSourceID()
-                                    : page_info->ukm_source_id;
+  ukm::SourceId ukm_source_id =
+      page_info ? page_info->ukm_source_id : ukm::UkmRecorder::GetNewSourceID();
   Memory_Experimental builder(ukm_source_id);
   builder.SetProcessType(static_cast<int64_t>(
       memory_instrumentation::mojom::ProcessType::RENDERER));
@@ -414,7 +419,7 @@
   const char* process = number_of_extensions == 0 ? "Renderer" : "Extension";
   EmitProcessUmaAndUkm(pmd, process, uptime, record_uma, &builder);
 
-  if (!page_info.is_null()) {
+  if (page_info) {
     builder.SetIsVisible(page_info->is_visible);
     builder.SetTimeSinceLastVisibilityChange(
         page_info->time_since_last_visibility_change.InSeconds());
@@ -494,10 +499,12 @@
   // The callback keeps this object alive until the callback is invoked.
   performance_manager::PerformanceManager* performance_manager =
       performance_manager::PerformanceManager::GetInstance();
-  performance_manager->BindInterface(mojo::MakeRequest(&introspector_));
   auto callback2 =
-      base::Bind(&ProcessMemoryMetricsEmitter::ReceivedProcessInfos, this);
-  introspector_->GetProcessToURLMap(callback2);
+      base::BindOnce(&ProcessMemoryMetricsEmitter::ReceivedProcessInfos, this);
+  performance_manager->CallOnGraph(
+      FROM_HERE,
+      base::BindOnce(&ProcessMemoryMetricsEmitter::GetProcessToPageInfoMap,
+                     std::move(callback2)));
 }
 
 void ProcessMemoryMetricsEmitter::MarkServiceRequestsInProgress() {
@@ -518,15 +525,14 @@
 }
 
 void ProcessMemoryMetricsEmitter::ReceivedProcessInfos(
-    std::vector<resource_coordinator::mojom::ProcessInfoPtr> process_infos) {
+    std::vector<ProcessInfo> process_infos) {
   get_process_urls_in_progress_ = false;
   process_infos_.clear();
   process_infos_.reserve(process_infos.size());
 
   // If there are duplicate pids, keep the latest ProcessInfoPtr.
-  for (resource_coordinator::mojom::ProcessInfoPtr& process_info :
-       process_infos) {
-    base::ProcessId pid = process_info->pid;
+  for (ProcessInfo& process_info : process_infos) {
+    base::ProcessId pid = process_info.pid;
     process_infos_[pid] = std::move(process_info);
   }
   CollateResults();
@@ -582,8 +588,8 @@
     base::ProcessId pid) {
   auto process_info = process_infos_.find(pid);
   if (process_info != process_infos_.end()) {
-    if (process_info->second->launch_time)
-      return now - process_info->second->launch_time.value();
+    if (!process_info->second.launch_time.is_null())
+      return now - process_info->second.launch_time;
   }
   return base::Optional<base::TimeDelta>();
 }
@@ -621,24 +627,22 @@
       }
       case memory_instrumentation::mojom::ProcessType::RENDERER: {
         renderer_private_footprint_total_kb += process_pmf_kb;
-        resource_coordinator::mojom::PageInfoPtr single_page_info;
+        const PageInfo* single_page_info = nullptr;
         auto iter = process_infos_.find(pmd.pid());
         if (iter != process_infos_.end()) {
-          const resource_coordinator::mojom::ProcessInfoPtr& process_info =
-              iter->second;
+          const ProcessInfo& process_info = iter->second;
 
           if (emit_metrics_for_all_processes) {
             // Renderer metrics-by-tab only make sense if we're visiting all
             // render processes.
-            for (const resource_coordinator::mojom::PageInfoPtr& page_info :
-                 process_info->page_infos) {
-              if (page_info->hosts_main_frame) {
-                per_tab_metrics.AssociateMainFrame(page_info->ukm_source_id,
-                                                   pmd.pid(), page_info->tab_id,
+            for (const PageInfo& page_info : process_info.page_infos) {
+              if (page_info.hosts_main_frame) {
+                per_tab_metrics.AssociateMainFrame(page_info.ukm_source_id,
+                                                   pmd.pid(), page_info.tab_id,
                                                    process_pmf_kb);
               } else {
-                per_tab_metrics.AssociateSubFrame(page_info->ukm_source_id,
-                                                  pmd.pid(), page_info->tab_id,
+                per_tab_metrics.AssociateSubFrame(page_info.ukm_source_id,
+                                                  pmd.pid(), page_info.tab_id,
                                                   process_pmf_kb);
               }
             }
@@ -648,8 +652,8 @@
           // emit any per-renderer URLs. This is not ideal, but UKM does not
           // support multiple-URLs per entry, and we must have one entry per
           // process.
-          if (process_info->page_infos.size() == 1) {
-            single_page_info = std::move(process_info->page_infos[0]);
+          if (process_info.page_infos.size() == 1) {
+            single_page_info = &process_info.page_infos[0];
           }
         }
 
@@ -728,3 +732,67 @@
     per_tab_metrics.RecordPmfs(GetUkmRecorder());
   }
 }
+
+namespace {
+
+// Returns true iff the given |process| is responsible for hosting the
+// main-frame of the given |page|.
+bool HostsMainFrame(performance_manager::ProcessNodeImpl* process,
+                    performance_manager::PageNodeImpl* page) {
+  performance_manager::FrameNodeImpl* main_frame = page->GetMainFrameNode();
+  if (main_frame == nullptr) {
+    // |process| can't host a frame that doesn't exist.
+    return false;
+  }
+
+  return main_frame->process_node() == process;
+}
+
+}  // namespace
+
+void ProcessMemoryMetricsEmitter::GetProcessToPageInfoMap(
+    GetProcessToPageInfoMapCallback callback,
+    performance_manager::Graph* graph) {
+  std::vector<ProcessInfo> process_infos;
+  std::vector<performance_manager::ProcessNodeImpl*> process_nodes =
+      graph->GetAllProcessNodes();
+  for (auto* process_node : process_nodes) {
+    if (process_node->process_id() == base::kNullProcessId)
+      continue;
+
+    ProcessInfo process_info;
+    process_info.pid = process_node->process_id();
+    process_info.launch_time = process_node->launch_time();
+
+    std::set<performance_manager::PageNodeImpl*> page_nodes =
+        process_node->GetAssociatedPageCoordinationUnits();
+    for (performance_manager::PageNodeImpl* page_node : page_nodes) {
+      if (page_node->ukm_source_id() == ukm::kInvalidSourceId)
+        continue;
+
+      PageInfo page_info;
+      page_info.ukm_source_id = page_node->ukm_source_id();
+      page_info.tab_id = page_node->id().id;
+      page_info.hosts_main_frame = HostsMainFrame(process_node, page_node);
+      page_info.is_visible = page_node->is_visible();
+      page_info.time_since_last_visibility_change =
+          page_node->TimeSinceLastVisibilityChange();
+      page_info.time_since_last_navigation =
+          page_node->TimeSinceLastNavigation();
+      process_info.page_infos.push_back(std::move(page_info));
+    }
+    process_infos.push_back(std::move(process_info));
+  }
+  std::move(callback).Run(std::move(process_infos));
+}
+
+ProcessMemoryMetricsEmitter::ProcessInfo::ProcessInfo() = default;
+
+ProcessMemoryMetricsEmitter::ProcessInfo::ProcessInfo(ProcessInfo&& other) =
+    default;
+
+ProcessMemoryMetricsEmitter::ProcessInfo::~ProcessInfo() = default;
+
+ProcessMemoryMetricsEmitter::ProcessInfo&
+ProcessMemoryMetricsEmitter::ProcessInfo::operator=(const ProcessInfo& other) =
+    default;
diff --git a/chrome/browser/metrics/process_memory_metrics_emitter.h b/chrome/browser/metrics/process_memory_metrics_emitter.h
index 0a2cfb7..0cc330a 100644
--- a/chrome/browser/metrics/process_memory_metrics_emitter.h
+++ b/chrome/browser/metrics/process_memory_metrics_emitter.h
@@ -5,20 +5,25 @@
 #ifndef CHROME_BROWSER_METRICS_PROCESS_MEMORY_METRICS_EMITTER_H_
 #define CHROME_BROWSER_METRICS_PROCESS_MEMORY_METRICS_EMITTER_H_
 
-#include <unordered_map>
 #include <vector>
 
+#include "base/callback.h"
+#include "base/containers/flat_map.h"
 #include "base/memory/ref_counted.h"
 #include "base/optional.h"
 #include "base/process/process_handle.h"
 #include "base/time/time.h"
+#include "services/metrics/public/cpp/ukm_source_id.h"
 #include "services/resource_coordinator/public/cpp/memory_instrumentation/global_memory_dump.h"
-#include "services/resource_coordinator/public/mojom/coordination_unit_introspector.mojom.h"
 
 namespace ukm {
 class UkmRecorder;
 }
 
+namespace performance_manager {
+class Graph;
+}
+
 // This class asynchronously fetches memory metrics for each process, and then
 // emits UMA metrics from those metrics.
 // Each instance is self-owned, and will delete itself once it has finished
@@ -29,6 +34,9 @@
 class ProcessMemoryMetricsEmitter
     : public base::RefCountedThreadSafe<ProcessMemoryMetricsEmitter> {
  public:
+  struct PageInfo;
+  struct ProcessInfo;
+
   // Use this constructor to emit UKM and UMA from all processes, i.e.
   // browser process, gpu process, and all renderers.
   ProcessMemoryMetricsEmitter();
@@ -50,10 +58,9 @@
       bool success,
       std::unique_ptr<memory_instrumentation::GlobalMemoryDump> dump);
 
-  // Virtual for testing. Callback invoked when resource_coordinator service
+  // Virtual for testing. Callback invoked when the performance_manager
   // returns info for each process.
-  virtual void ReceivedProcessInfos(
-      std::vector<resource_coordinator::mojom::ProcessInfoPtr> process_infos);
+  virtual void ReceivedProcessInfos(std::vector<ProcessInfo> process_infos);
 
   // Virtual for testing.
   virtual ukm::UkmRecorder* GetUkmRecorder();
@@ -75,7 +82,10 @@
   // be collated.
   void CollateResults();
 
-  resource_coordinator::mojom::CoordinationUnitIntrospectorPtr introspector_;
+  using GetProcessToPageInfoMapCallback =
+      base::OnceCallback<void(std::vector<ProcessInfo>)>;
+  static void GetProcessToPageInfoMap(GetProcessToPageInfoMapCallback callback,
+                                      performance_manager::Graph* graph);
 
   // The results of each request are cached. When both requests are finished,
   // the results are collated.
@@ -83,9 +93,8 @@
   std::unique_ptr<memory_instrumentation::GlobalMemoryDump> global_dump_;
   bool get_process_urls_in_progress_ = false;
 
-  // The key is ProcessInfoPtr::pid.
-  std::unordered_map<int64_t, resource_coordinator::mojom::ProcessInfoPtr>
-      process_infos_;
+  // The key is ProcessInfo::pid.
+  base::flat_map<base::ProcessId, ProcessInfo> process_infos_;
 
   // Specify this pid_scope_ to only record the memory metrics of the specific
   // process.
@@ -94,4 +103,29 @@
   DISALLOW_COPY_AND_ASSIGN(ProcessMemoryMetricsEmitter);
 };
 
+// A |PageInfo| describes some metrics about a particular page with respect to
+// a given process.
+struct ProcessMemoryMetricsEmitter::PageInfo {
+  // Identifier to distinguish which UMK Source this |PageInfo| corresponds to.
+  ukm::SourceId ukm_source_id;
+  // Identifier to distinguish which tab this |PageInfo| corresponds to.
+  uint64_t tab_id;
+  // True iff the process for this |PageInfo| hosts the main frame of the page.
+  bool hosts_main_frame;
+  bool is_visible;
+  base::TimeDelta time_since_last_navigation;
+  base::TimeDelta time_since_last_visibility_change;
+};
+
+struct ProcessMemoryMetricsEmitter::ProcessInfo {
+  ProcessInfo();
+  ProcessInfo(ProcessInfo&& other);
+  ~ProcessInfo();
+  ProcessInfo& operator=(const ProcessInfo& other);
+
+  base::ProcessId pid;
+  std::vector<PageInfo> page_infos;
+  base::Time launch_time;
+};
+
 #endif  // CHROME_BROWSER_METRICS_PROCESS_MEMORY_METRICS_EMITTER_H_
diff --git a/chrome/browser/metrics/process_memory_metrics_emitter_browsertest.cc b/chrome/browser/metrics/process_memory_metrics_emitter_browsertest.cc
index e94a07f..f685672d 100644
--- a/chrome/browser/metrics/process_memory_metrics_emitter_browsertest.cc
+++ b/chrome/browser/metrics/process_memory_metrics_emitter_browsertest.cc
@@ -105,9 +105,7 @@
     QuitIfFinished();
   }
 
-  void ReceivedProcessInfos(
-      std::vector<resource_coordinator::mojom::ProcessInfoPtr> process_infos)
-      override {
+  void ReceivedProcessInfos(std::vector<ProcessInfo> process_infos) override {
     ProcessMemoryMetricsEmitter::ReceivedProcessInfos(std::move(process_infos));
     finished_process_info_ = true;
     QuitIfFinished();
diff --git a/chrome/browser/metrics/process_memory_metrics_emitter_unittest.cc b/chrome/browser/metrics/process_memory_metrics_emitter_unittest.cc
index 2d422db..8923d92 100644
--- a/chrome/browser/metrics/process_memory_metrics_emitter_unittest.cc
+++ b/chrome/browser/metrics/process_memory_metrics_emitter_unittest.cc
@@ -21,10 +21,10 @@
 using ProcessMemoryDumpPtr =
     memory_instrumentation::mojom::ProcessMemoryDumpPtr;
 using OSMemDumpPtr = memory_instrumentation::mojom::OSMemDumpPtr;
-using PageInfoPtr = resource_coordinator::mojom::PageInfoPtr;
+using PageInfo = ProcessMemoryMetricsEmitter::PageInfo;
 using ProcessType = memory_instrumentation::mojom::ProcessType;
-using ProcessInfoPtr = resource_coordinator::mojom::ProcessInfoPtr;
-using ProcessInfoVector = std::vector<ProcessInfoPtr>;
+using ProcessInfo = ProcessMemoryMetricsEmitter::ProcessInfo;
+using ProcessInfoVector = std::vector<ProcessInfo>;
 
 namespace {
 
@@ -501,60 +501,57 @@
 
   // Process 200 always has no URLs.
   {
-    ProcessInfoPtr process_info(
-        resource_coordinator::mojom::ProcessInfo::New());
-    process_info->pid = 200;
+    ProcessInfo process_info;
+    process_info.pid = 200;
     process_infos.push_back(std::move(process_info));
   }
 
   // Process kTestRendererPid201 always has 1 URL
   {
-    ProcessInfoPtr process_info(
-        resource_coordinator::mojom::ProcessInfo::New());
-    process_info->pid = kTestRendererPid201;
+    ProcessInfo process_info;
+    process_info.pid = kTestRendererPid201;
     ukm::SourceId first_source_id = ukm::UkmRecorder::GetNewSourceID();
     ukm_recorder.UpdateSourceURL(first_source_id,
                                  GURL("http://www.url201.com/"));
-    PageInfoPtr page_info(resource_coordinator::mojom::PageInfo::New());
+    PageInfo page_info;
 
-    page_info->ukm_source_id = first_source_id;
-    page_info->tab_id = 201;
-    page_info->hosts_main_frame = true;
-    page_info->is_visible = true;
-    page_info->time_since_last_visibility_change =
+    page_info.ukm_source_id = first_source_id;
+    page_info.tab_id = 201;
+    page_info.hosts_main_frame = true;
+    page_info.is_visible = true;
+    page_info.time_since_last_visibility_change =
         base::TimeDelta::FromSeconds(15);
-    page_info->time_since_last_navigation = base::TimeDelta::FromSeconds(20);
-    process_info->page_infos.push_back(std::move(page_info));
+    page_info.time_since_last_navigation = base::TimeDelta::FromSeconds(20);
+    process_info.page_infos.push_back(page_info);
     process_infos.push_back(std::move(process_info));
   }
 
   // Process kTestRendererPid202 always has 2 URL
   {
-    ProcessInfoPtr process_info(
-        resource_coordinator::mojom::ProcessInfo::New());
-    process_info->pid = kTestRendererPid202;
+    ProcessInfo process_info;
+    process_info.pid = kTestRendererPid202;
     ukm::SourceId first_source_id = ukm::UkmRecorder::GetNewSourceID();
     ukm::SourceId second_source_id = ukm::UkmRecorder::GetNewSourceID();
     ukm_recorder.UpdateSourceURL(first_source_id,
                                  GURL("http://www.url2021.com/"));
     ukm_recorder.UpdateSourceURL(second_source_id,
                                  GURL("http://www.url2022.com/"));
-    PageInfoPtr page_info1(resource_coordinator::mojom::PageInfo::New());
-    page_info1->ukm_source_id = first_source_id;
-    page_info1->tab_id = 2021;
-    page_info1->hosts_main_frame = true;
-    page_info1->time_since_last_visibility_change =
+    PageInfo page_info1;
+    page_info1.ukm_source_id = first_source_id;
+    page_info1.tab_id = 2021;
+    page_info1.hosts_main_frame = true;
+    page_info1.time_since_last_visibility_change =
         base::TimeDelta::FromSeconds(11);
-    page_info1->time_since_last_navigation = base::TimeDelta::FromSeconds(21);
-    PageInfoPtr page_info2(resource_coordinator::mojom::PageInfo::New());
-    page_info2->ukm_source_id = second_source_id;
-    page_info2->tab_id = 2022;
-    page_info2->hosts_main_frame = true;
-    page_info2->time_since_last_visibility_change =
+    page_info1.time_since_last_navigation = base::TimeDelta::FromSeconds(21);
+    PageInfo page_info2;
+    page_info2.ukm_source_id = second_source_id;
+    page_info2.tab_id = 2022;
+    page_info2.hosts_main_frame = true;
+    page_info2.time_since_last_visibility_change =
         base::TimeDelta::FromSeconds(12);
-    page_info2->time_since_last_navigation = base::TimeDelta::FromSeconds(22);
-    process_info->page_infos.push_back(std::move(page_info1));
-    process_info->page_infos.push_back(std::move(page_info2));
+    page_info2.time_since_last_navigation = base::TimeDelta::FromSeconds(22);
+    process_info.page_infos.push_back(std::move(page_info1));
+    process_info.page_infos.push_back(std::move(page_info2));
 
     process_infos.push_back(std::move(process_info));
   }
diff --git a/chrome/browser/performance_manager/graph/graph_introspector_impl.cc b/chrome/browser/performance_manager/graph/graph_introspector_impl.cc
deleted file mode 100644
index 5e0ac80..0000000
--- a/chrome/browser/performance_manager/graph/graph_introspector_impl.cc
+++ /dev/null
@@ -1,88 +0,0 @@
-// Copyright 2017 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "chrome/browser/performance_manager/graph/graph_introspector_impl.h"
-
-#include <set>
-#include <utility>
-#include <vector>
-
-#include "base/process/process_handle.h"
-#include "base/time/time.h"
-#include "chrome/browser/performance_manager/graph/frame_node_impl.h"
-#include "chrome/browser/performance_manager/graph/graph.h"
-#include "chrome/browser/performance_manager/graph/page_node_impl.h"
-#include "chrome/browser/performance_manager/graph/process_node_impl.h"
-#include "services/service_manager/public/cpp/bind_source_info.h"
-
-namespace {
-
-using performance_manager::FrameNodeImpl;
-using performance_manager::PageNodeImpl;
-using performance_manager::ProcessNodeImpl;
-
-// Returns true iff the given |process| is responsible for hosting the
-// main-frame of the given |page|.
-bool HostsMainFrame(ProcessNodeImpl* process, PageNodeImpl* page) {
-  FrameNodeImpl* main_frame = page->GetMainFrameNode();
-  if (main_frame == nullptr) {
-    // |process| can't host a frame that doesn't exist.
-    return false;
-  }
-
-  return main_frame->process_node() == process;
-}
-
-}  // namespace
-
-namespace performance_manager {
-
-CoordinationUnitIntrospectorImpl::CoordinationUnitIntrospectorImpl(Graph* graph)
-    : graph_(graph) {}
-
-CoordinationUnitIntrospectorImpl::~CoordinationUnitIntrospectorImpl() = default;
-
-void CoordinationUnitIntrospectorImpl::GetProcessToURLMap(
-    GetProcessToURLMapCallback callback) {
-  std::vector<resource_coordinator::mojom::ProcessInfoPtr> process_infos;
-  std::vector<ProcessNodeImpl*> process_nodes = graph_->GetAllProcessNodes();
-  for (auto* process_node : process_nodes) {
-    if (process_node->process_id() == base::kNullProcessId)
-      continue;
-
-    resource_coordinator::mojom::ProcessInfoPtr process_info(
-        resource_coordinator::mojom::ProcessInfo::New());
-    process_info->pid = process_node->process_id();
-    process_info->launch_time = process_node->launch_time();
-
-    std::set<PageNodeImpl*> page_nodes =
-        process_node->GetAssociatedPageCoordinationUnits();
-    for (PageNodeImpl* page_node : page_nodes) {
-      if (page_node->ukm_source_id() == ukm::kInvalidSourceId)
-        continue;
-
-      resource_coordinator::mojom::PageInfoPtr page_info(
-          resource_coordinator::mojom::PageInfo::New());
-      page_info->ukm_source_id = page_node->ukm_source_id();
-      page_info->tab_id = page_node->id().id;
-      page_info->hosts_main_frame = HostsMainFrame(process_node, page_node);
-      page_info->is_visible = page_node->is_visible();
-      page_info->time_since_last_visibility_change =
-          page_node->TimeSinceLastVisibilityChange();
-      page_info->time_since_last_navigation =
-          page_node->TimeSinceLastNavigation();
-      process_info->page_infos.push_back(std::move(page_info));
-    }
-    process_infos.push_back(std::move(process_info));
-  }
-  std::move(callback).Run(std::move(process_infos));
-}
-
-void CoordinationUnitIntrospectorImpl::BindToInterface(
-    resource_coordinator::mojom::CoordinationUnitIntrospectorRequest request,
-    const service_manager::BindSourceInfo& source_info) {
-  bindings_.AddBinding(this, std::move(request));
-}
-
-}  // namespace performance_manager
diff --git a/chrome/browser/performance_manager/graph/graph_introspector_impl.h b/chrome/browser/performance_manager/graph/graph_introspector_impl.h
deleted file mode 100644
index 6a2d22e..0000000
--- a/chrome/browser/performance_manager/graph/graph_introspector_impl.h
+++ /dev/null
@@ -1,43 +0,0 @@
-// Copyright 2017 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef CHROME_BROWSER_PERFORMANCE_MANAGER_GRAPH_GRAPH_INTROSPECTOR_IMPL_H_
-#define CHROME_BROWSER_PERFORMANCE_MANAGER_GRAPH_GRAPH_INTROSPECTOR_IMPL_H_
-
-#include "mojo/public/cpp/bindings/binding_set.h"
-#include "services/resource_coordinator/public/mojom/coordination_unit_introspector.mojom.h"
-#include "services/service_manager/public/cpp/bind_source_info.h"
-
-namespace service_manager {
-struct BindSourceInfo;
-}  // namespace service_manager
-
-namespace performance_manager {
-
-class Graph;
-
-class CoordinationUnitIntrospectorImpl
-    : public resource_coordinator::mojom::CoordinationUnitIntrospector {
- public:
-  explicit CoordinationUnitIntrospectorImpl(Graph* graph);
-  ~CoordinationUnitIntrospectorImpl() override;
-
-  void BindToInterface(
-      resource_coordinator::mojom::CoordinationUnitIntrospectorRequest request,
-      const service_manager::BindSourceInfo& source_info);
-
-  // Overridden from resource_coordinator::mojom::CoordinationUnitIntrospector:
-  void GetProcessToURLMap(GetProcessToURLMapCallback callback) override;
-
- private:
-  Graph* const graph_;
-  mojo::BindingSet<resource_coordinator::mojom::CoordinationUnitIntrospector>
-      bindings_;
-
-  DISALLOW_COPY_AND_ASSIGN(CoordinationUnitIntrospectorImpl);
-};
-
-}  // namespace performance_manager
-
-#endif  // CHROME_BROWSER_PERFORMANCE_MANAGER_GRAPH_GRAPH_INTROSPECTOR_IMPL_H_
diff --git a/chrome/browser/performance_manager/performance_manager.cc b/chrome/browser/performance_manager/performance_manager.cc
index 57a6713..3adea9f 100644
--- a/chrome/browser/performance_manager/performance_manager.cc
+++ b/chrome/browser/performance_manager/performance_manager.cc
@@ -41,8 +41,7 @@
   return g_performance_manager;
 }
 
-PerformanceManager::PerformanceManager()
-    : task_runner_(CreateTaskRunner()), introspector_(&graph_) {
+PerformanceManager::PerformanceManager() : task_runner_(CreateTaskRunner()) {
   DETACH_FROM_SEQUENCE(sequence_checker_);
 }
 
@@ -252,10 +251,6 @@
     std::unique_ptr<service_manager::Connector> connector) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
 
-  interface_registry_.AddInterface(
-      base::BindRepeating(&CoordinationUnitIntrospectorImpl::BindToInterface,
-                          base::Unretained(&introspector_)));
-
   // Register new |GraphObserver| implementations here.
   auto page_signal_generator_impl = std::make_unique<PageSignalGeneratorImpl>();
   interface_registry_.AddInterface(
diff --git a/chrome/browser/performance_manager/performance_manager.h b/chrome/browser/performance_manager/performance_manager.h
index 8d8c30b..6b3a157 100644
--- a/chrome/browser/performance_manager/performance_manager.h
+++ b/chrome/browser/performance_manager/performance_manager.h
@@ -15,7 +15,6 @@
 #include "base/sequence_checker.h"
 #include "base/sequenced_task_runner.h"
 #include "chrome/browser/performance_manager/graph/graph.h"
-#include "chrome/browser/performance_manager/graph/graph_introspector_impl.h"
 #include "chrome/browser/performance_manager/performance_manager.h"
 #include "chrome/browser/performance_manager/webui_graph_dump_impl.h"
 #include "services/resource_coordinator/public/mojom/coordination_unit.mojom.h"
@@ -136,8 +135,6 @@
   // The registered graph observers.
   std::vector<std::unique_ptr<GraphObserver>> observers_;
 
-  CoordinationUnitIntrospectorImpl introspector_;
-
   // Provided to |graph_|.
   // TODO(siggi): This no longer needs to go through mojo.
   std::unique_ptr<ukm::MojoUkmRecorder> ukm_recorder_;
diff --git a/chrome/browser/policy/test/policy_testserver.py b/chrome/browser/policy/test/policy_testserver.py
index 5a16814f..6be5bb6 100644
--- a/chrome/browser/policy/test/policy_testserver.py
+++ b/chrome/browser/policy/test/policy_testserver.py
@@ -58,6 +58,8 @@
       "token": "abcd-ef01-123123123",
       "username": "admin@example.com"
    },
+   "device_register_http_error" : 902,
+   "allow_set_device_attributes" : false,
 }
 
 """
@@ -427,6 +429,9 @@
     if not auth:
       return (403, 'No authorization')
 
+    if 'device_register_http_error' in policy:
+      return (policy['device_register_http_error'], 'Preconfigured error')
+
     if ('managed_users' not in policy):
       return (500, 'Error in config - no managed users')
     username = self.server.ResolveUser(auth)
@@ -716,8 +721,15 @@
       A tuple of HTTP status code and response data to send to the client.
     """
     response = dm.DeviceManagementResponse()
+    policy = self.server.GetPolicies()
+    update_allowed = True
+    if ('allow_set_device_attributes' in policy):
+      update_allowed = policy['allow_set_device_attributes']
+
     response.device_attribute_update_permission_response.result = (
-        dm.DeviceAttributeUpdatePermissionResponse.ATTRIBUTE_UPDATE_ALLOWED)
+        dm.DeviceAttributeUpdatePermissionResponse.ATTRIBUTE_UPDATE_ALLOWED
+        if update_allowed else
+        dm.DeviceAttributeUpdatePermissionResponse.ATTRIBUTE_UPDATE_DISALLOWED)
 
     return (200, response)
 
diff --git a/chrome/browser/resources/print_preview/data/destination.js b/chrome/browser/resources/print_preview/data/destination.js
index e9893ba..22854d3 100644
--- a/chrome/browser/resources/print_preview/data/destination.js
+++ b/chrome/browser/resources/print_preview/data/destination.js
@@ -201,8 +201,8 @@
  */
 print_preview.PinModeRestriction = {
   NONE: 0,
-  SECURE: 1,
-  UNSECURE: 2
+  PIN: 1,
+  NO_PIN: 2
 };
 
 /**
diff --git a/chrome/browser/resources/print_preview/new/model.js b/chrome/browser/resources/print_preview/new/model.js
index d425c53f..19c144f 100644
--- a/chrome/browser/resources/print_preview/new/model.js
+++ b/chrome/browser/resources/print_preview/new/model.js
@@ -1002,7 +1002,7 @@
         !!duplexPolicy && setDuplexTypeByPolicy);
 
     const pinPolicy = this.destination.pinPolicy;
-    if (pinPolicy == print_preview.PinModeRestriction.UNSECURE) {
+    if (pinPolicy == print_preview.PinModeRestriction.NO_PIN) {
       this.set('settings.pin.available', false);
       this.set('settings.pinValue.available', false);
     }
@@ -1010,7 +1010,7 @@
     if (pinValue) {
       this.set(
           'settings.pin.value',
-          pinValue == print_preview.PinModeRestriction.SECURE);
+          pinValue == print_preview.PinModeRestriction.PIN);
     }
     this.set('settings.pin.setByPolicy', !!pinPolicy);
 
diff --git a/chrome/browser/search/one_google_bar/one_google_bar_loader_impl_unittest.cc b/chrome/browser/search/one_google_bar/one_google_bar_loader_impl_unittest.cc
index 368ecc1..5c59e12 100644
--- a/chrome/browser/search/one_google_bar/one_google_bar_loader_impl_unittest.cc
+++ b/chrome/browser/search/one_google_bar/one_google_bar_loader_impl_unittest.cc
@@ -321,7 +321,10 @@
   EXPECT_TRUE(last_request_headers().GetHeader(signin::kChromeConnectedHeader,
                                                &header_value));
   // mode = PROFILE_MODE_DEFAULT
-  EXPECT_EQ("mode=0,enable_account_consistency=false", header_value);
+  EXPECT_EQ(
+      "mode=0,enable_account_consistency=false,"
+      "consistency_enabled_by_default=false",
+      header_value);
 #else
   // On not Chrome OS, the X-Chrome-Connected header must not be present.
   EXPECT_FALSE(
@@ -356,7 +359,10 @@
   EXPECT_TRUE(last_request_headers().GetHeader(signin::kChromeConnectedHeader,
                                                &header_value));
   // mode = PROFILE_MODE_INCOGNITO_DISABLED | PROFILE_MODE_ADD_ACCOUNT_DISABLED
-  EXPECT_EQ("mode=3,enable_account_consistency=true", header_value);
+  EXPECT_EQ(
+      "mode=3,enable_account_consistency=true,"
+      "consistency_enabled_by_default=false",
+      header_value);
 #else
   // This is not a valid case (mirror account consistency can only be required
   // on Chrome OS). This ensures in this case nothing happens.
diff --git a/chrome/browser/signin/chromeos_mirror_account_consistency_browsertest.cc b/chrome/browser/signin/chromeos_mirror_account_consistency_browsertest.cc
index 183a883d..65c7bb9 100644
--- a/chrome/browser/signin/chromeos_mirror_account_consistency_browsertest.cc
+++ b/chrome/browser/signin/chromeos_mirror_account_consistency_browsertest.cc
@@ -139,7 +139,8 @@
   ASSERT_EQ(3, signin::PROFILE_MODE_INCOGNITO_DISABLED |
                    signin::PROFILE_MODE_ADD_ACCOUNT_DISABLED);
   TestMirrorRequestForProfile(test_server_.get(), profile,
-                              "mode=3,enable_account_consistency=true");
+                              "mode=3,enable_account_consistency=true,"
+                              "consistency_enabled_by_default=false");
 }
 
 class ChromeOsMirrorAccountConsistencyTestWithAccountManagerEnabled
@@ -185,5 +186,6 @@
   EXPECT_TRUE(
       AccountConsistencyModeManager::IsMirrorEnabledForProfile(profile));
   TestMirrorRequestForProfile(test_server_.get(), profile,
-                              "mode=0,enable_account_consistency=true");
+                              "mode=0,enable_account_consistency=true,"
+                              "consistency_enabled_by_default=false");
 }
diff --git a/chrome/browser/sync/test/integration/single_client_passwords_sync_test.cc b/chrome/browser/sync/test/integration/single_client_passwords_sync_test.cc
index 14e9db2..29ed8b5 100644
--- a/chrome/browser/sync/test/integration/single_client_passwords_sync_test.cc
+++ b/chrome/browser/sync/test/integration/single_client_passwords_sync_test.cc
@@ -14,6 +14,8 @@
 #include "components/sync/driver/profile_sync_service.h"
 #include "components/sync/driver/sync_driver_switches.h"
 
+namespace {
+
 using passwords_helper::AddLogin;
 using passwords_helper::CreateTestPasswordForm;
 using passwords_helper::GetPasswordCount;
@@ -24,6 +26,9 @@
 
 using autofill::PasswordForm;
 
+using testing::ElementsAre;
+using testing::IsEmpty;
+
 class SingleClientPasswordsSyncTest : public FeatureToggler, public SyncTest {
  public:
   SingleClientPasswordsSyncTest()
@@ -199,3 +204,58 @@
 INSTANTIATE_TEST_SUITE_P(USS,
                          SingleClientPasswordsSyncTest,
                          ::testing::Values(false, true));
+
+class SingleClientPasswordsSyncUssMigratorTest : public SyncTest {
+ public:
+  SingleClientPasswordsSyncUssMigratorTest() : SyncTest(SINGLE_CLIENT) {}
+  ~SingleClientPasswordsSyncUssMigratorTest() override {}
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(SingleClientPasswordsSyncUssMigratorTest);
+};
+
+// Creates and syncs two passwords before USS being enabled.
+IN_PROC_BROWSER_TEST_F(SingleClientPasswordsSyncUssMigratorTest,
+                       PRE_ExerciseUssMigrator) {
+  base::test::ScopedFeatureList override_features;
+  override_features.InitAndDisableFeature(switches::kSyncUSSPasswords);
+
+  ASSERT_TRUE(SetupSync()) << "SetupSync() failed.";
+  AddLogin(GetPasswordStore(0), CreateTestPasswordForm(0));
+  AddLogin(GetPasswordStore(0), CreateTestPasswordForm(1));
+  ASSERT_TRUE(UpdatedProgressMarkerChecker(GetSyncService(0)).Wait());
+  ASSERT_EQ(2, GetPasswordCount(0));
+}
+
+// Now that local passwords, the local sync directory and the sever are
+// populated with two passwords, USS is enabled for passwords.
+IN_PROC_BROWSER_TEST_F(SingleClientPasswordsSyncUssMigratorTest,
+                       ExerciseUssMigrator) {
+  base::test::ScopedFeatureList override_features;
+  override_features.InitAndEnableFeature(switches::kSyncUSSPasswords);
+
+  base::HistogramTester histogram_tester;
+  ASSERT_TRUE(SetupClients()) << "SetupClients() failed.";
+  ASSERT_EQ(2, GetPasswordCount(0));
+#if defined(CHROMEOS)
+  // identity::SetRefreshTokenForPrimaryAccount() is needed on ChromeOS in order
+  // to get a non-empty refresh token on startup.
+  GetClient(0)->SignInPrimaryAccount();
+#endif  // defined(CHROMEOS)
+  ASSERT_TRUE(GetClient(0)->AwaitSyncSetupCompletion());
+  ASSERT_EQ(2, GetPasswordCount(0));
+
+  EXPECT_EQ(1, histogram_tester.GetBucketCount(
+                   "Sync.USSMigrationSuccess",
+                   syncer::ModelTypeToHistogramInt(syncer::PASSWORDS)));
+  EXPECT_THAT(
+      histogram_tester.GetAllSamples("Sync.USSMigrationEntityCount.PASSWORD"),
+      ElementsAre(base::Bucket(/*min=*/2, /*count=*/1)));
+  EXPECT_THAT(histogram_tester.GetAllSamples("Sync.DataTypeStartFailures2"),
+              IsEmpty());
+  EXPECT_EQ(
+      0, histogram_tester.GetBucketCount("Sync.ModelTypeEntityChange3.PASSWORD",
+                                         /*REMOTE_INITIAL_UPDATE=*/5));
+}
+
+}  // namespace
diff --git a/chrome/browser/sync/test/integration/single_client_user_events_sync_test.cc b/chrome/browser/sync/test/integration/single_client_user_events_sync_test.cc
index 94618a0..4b1be45 100644
--- a/chrome/browser/sync/test/integration/single_client_user_events_sync_test.cc
+++ b/chrome/browser/sync/test/integration/single_client_user_events_sync_test.cc
@@ -304,19 +304,4 @@
   EXPECT_TRUE(ExpectUserEvents({test_event1}));
 }
 
-IN_PROC_BROWSER_TEST_F(SingleClientUserEventsSyncTest, FieldTrial) {
-  const std::string trial_name = "TrialName";
-  const std::string group_name = "GroupName";
-
-  ASSERT_TRUE(SetupSync());
-  variations::AssociateGoogleVariationID(variations::CHROME_SYNC_EVENT_LOGGER,
-                                         trial_name, group_name, 123);
-  base::FieldTrialList::CreateFieldTrial(trial_name, group_name);
-  base::FieldTrialList::FindFullName(trial_name);
-
-  UserEventCaseChecker(GetSyncService(0), GetFakeServer(),
-                       {UserEventSpecifics::kFieldTrialEvent})
-      .Wait();
-}
-
 }  // namespace
diff --git a/chrome/browser/ui/ash/ash_test_util.cc b/chrome/browser/ui/ash/ash_test_util.cc
new file mode 100644
index 0000000..47aeaa7
--- /dev/null
+++ b/chrome/browser/ui/ash/ash_test_util.cc
@@ -0,0 +1,37 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/ui/ash/ash_test_util.h"
+
+#include "ash/public/interfaces/constants.mojom.h"
+#include "ash/public/interfaces/shell_test_api.test-mojom.h"
+#include "base/run_loop.h"
+#include "content/public/common/service_manager_connection.h"
+#include "services/service_manager/public/cpp/connector.h"
+#include "ui/aura/test/mus/change_completion_waiter.h"
+
+namespace test {
+
+ash::mojom::ShellTestApiPtr GetShellTestApi() {
+  ash::mojom::ShellTestApiPtr shell_test_api;
+  content::ServiceManagerConnection::GetForProcess()
+      ->GetConnector()
+      ->BindInterface(ash::mojom::kServiceName, &shell_test_api);
+
+  return shell_test_api;
+}
+
+void WaitForNoPointerHoldLock(bool wait_for_changes) {
+  ash::mojom::ShellTestApiPtr shell_test_api = GetShellTestApi();
+
+  // Allow nestable tasks because this is called within a move loop.
+  base::RunLoop run_loop(base::RunLoop::Type::kNestableTasksAllowed);
+  shell_test_api->WaitForNoPointerHoldLock(run_loop.QuitClosure());
+  run_loop.Run();
+
+  if (wait_for_changes)
+    aura::test::WaitForAllChangesToComplete();
+}
+
+}  // namespace test
diff --git a/chrome/browser/ui/ash/ash_test_util.h b/chrome/browser/ui/ash/ash_test_util.h
new file mode 100644
index 0000000..0fb0ded
--- /dev/null
+++ b/chrome/browser/ui/ash/ash_test_util.h
@@ -0,0 +1,23 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_BROWSER_UI_ASH_ASH_TEST_UTIL_H_
+#define CHROME_BROWSER_UI_ASH_ASH_TEST_UTIL_H_
+
+#include "ash/public/interfaces/shell_test_api.test-mojom-test-utils.h"
+#include "ash/public/interfaces/shell_test_api.test-mojom.h"
+
+namespace test {
+
+// Binds to ash service and returns a ShellTestApiPtr.
+ash::mojom::ShellTestApiPtr GetShellTestApi();
+
+// Waits until WindowTreeHost no longer holding pointer events.
+// If |wait_for_all_changes| is true, this also runs
+// aura::test::WaitForAllChangesToComplete().
+void WaitForNoPointerHoldLock(bool wait_for_changes = true);
+
+}  // namespace test
+
+#endif  // CHROME_BROWSER_UI_ASH_ASH_TEST_UTIL_H_
diff --git a/chrome/browser/ui/ash/drag_to_overview_interactive_uitest.cc b/chrome/browser/ui/ash/drag_to_overview_interactive_uitest.cc
new file mode 100644
index 0000000..cad041f
--- /dev/null
+++ b/chrome/browser/ui/ash/drag_to_overview_interactive_uitest.cc
@@ -0,0 +1,215 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "ash/public/cpp/ash_switches.h"
+#include "base/bind.h"
+#include "base/macros.h"
+#include "base/run_loop.h"
+#include "base/task/post_task.h"
+#include "chrome/browser/chrome_notification_types.h"
+#include "chrome/browser/ui/ash/ash_test_util.h"
+#include "chrome/browser/ui/ash/tablet_mode_client_test_util.h"
+#include "chrome/browser/ui/browser.h"
+#include "chrome/browser/ui/browser_list.h"
+#include "chrome/browser/ui/views/frame/browser_view.h"
+#include "chrome/browser/ui/views/tabs/tab_drag_controller.h"
+#include "chrome/browser/ui/views/tabs/tab_strip.h"
+#include "chrome/test/base/interactive_test_utils.h"
+#include "chrome/test/base/perf/performance_test.h"
+#include "content/public/browser/notification_service.h"
+#include "content/public/test/test_utils.h"
+#include "services/service_manager/public/cpp/connector.h"
+#include "ui/aura/window.h"
+#include "ui/base/test/ui_controls.h"
+#include "ui/compositor/compositor.h"
+#include "ui/display/display.h"
+#include "ui/display/screen.h"
+#include "ui/gfx/geometry/vector2d.h"
+#include "ui/views/widget/widget.h"
+
+namespace {
+
+bool IsOverviewSelecting() {
+  ash::mojom::ShellTestApiPtr shell_test_api = test::GetShellTestApi();
+  ash::mojom::ShellTestApiAsyncWaiter waiter(shell_test_api.get());
+  bool is_selecting = false;
+  waiter.IsOverviewSelecting(&is_selecting);
+  return is_selecting;
+}
+
+int GetDetachY(TabStrip* tab_strip) {
+  return std::max(TabDragController::kTouchVerticalDetachMagnetism,
+                  TabDragController::kVerticalDetachMagnetism) +
+         tab_strip->height() + 1;
+}
+
+// Waits for the primary display to present a frame after the object is
+// constructed.
+class NextFrameWaiter {
+ public:
+  NextFrameWaiter() : shell_test_api_(test::GetShellTestApi()) {
+    shell_test_api_->WaitForNextFrame(base::BindOnce(
+        &NextFrameWaiter::OnFramePresented, base::Unretained(this)));
+    // Flush to ensure the call has gone through.
+    shell_test_api_.FlushForTesting();
+  }
+  ~NextFrameWaiter() { EXPECT_TRUE(frame_presented_); }
+
+  void WaitForDisplay() {
+    if (!frame_presented_) {
+      run_loop_ = std::make_unique<base::RunLoop>(
+          base::RunLoop::Type::kNestableTasksAllowed);
+      run_loop_->Run();
+      EXPECT_TRUE(frame_presented_);
+    }
+  }
+
+ private:
+  void OnFramePresented() {
+    frame_presented_ = true;
+    if (run_loop_)
+      run_loop_->Quit();
+  }
+
+  ash::mojom::ShellTestApiPtr shell_test_api_;
+  bool frame_presented_ = false;
+  std::unique_ptr<base::RunLoop> run_loop_;
+
+  DISALLOW_COPY_AND_ASSIGN(NextFrameWaiter);
+};
+
+}  // namespace
+
+class DragToOverviewTest : public UIPerformanceTest {
+ public:
+  DragToOverviewTest() = default;
+  ~DragToOverviewTest() override = default;
+
+  // UIPerformanceTest:
+  void SetUpCommandLine(base::CommandLine* command_line) override {
+    command_line->AppendSwitch(ash::switches::kAshEnableTabletMode);
+  }
+  void SetUpOnMainThread() override {
+    test::SetAndWaitForTabletMode(true);
+
+    if (base::SysInfo::IsRunningOnChromeOS()) {
+      base::RunLoop run_loop;
+      base::PostDelayedTask(FROM_HERE, run_loop.QuitClosure(),
+                            base::TimeDelta::FromSeconds(5));
+      run_loop.Run();
+    }
+  }
+  std::vector<std::string> GetUMAHistogramNames() const override {
+    return {
+        "Ash.SwipeDownDrag.Window.PresentationTime.TabletMode",
+        "Ash.SwipeDownDrag.Window.PresentationTime.MaxLatency.TabletMode",
+        "Ash.SwipeDownDrag.Tab.PresentationTime.TabletMode",
+        "Ash.SwipeDownDrag.Tab.PresentationTime.MaxLatency.TabletMode",
+    };
+  }
+
+  // Continue a drag by perform |count| steps mouse dragging with each step move
+  // |delta|, then moue up.
+  void ContinueDrag(const gfx::Point& start_position,
+                    const gfx::Vector2d& delta,
+                    int count) {
+    gfx::Point drag_position = start_position;
+    for (int i = 0; i < count; ++i) {
+      drag_position += delta;
+
+      test::WaitForNoPointerHoldLock(/*wait_for_changes=*/false);
+      NextFrameWaiter waiter;
+      ASSERT_TRUE(
+          ui_controls::SendMouseMove(drag_position.x(), drag_position.y()));
+      waiter.WaitForDisplay();
+    }
+
+    {
+      NextFrameWaiter waiter;
+      ASSERT_TRUE(
+          ui_controls::SendMouseEvents(ui_controls::LEFT, ui_controls::UP));
+      waiter.WaitForDisplay();
+    }
+  }
+
+  void VerifyTabDetachedAndContinueDrag(const gfx::Point& start_position,
+                                        const gfx::Vector2d delta,
+                                        int count) {
+    // Tab should be detached to create a new browser window.
+    EXPECT_EQ(2u, BrowserList::GetInstance()->size());
+    EXPECT_TRUE(TabDragController::IsActive());
+
+    ContinueDrag(start_position, delta, count);
+  }
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(DragToOverviewTest);
+};
+
+IN_PROC_BROWSER_TEST_F(DragToOverviewTest, DragWindow) {
+  BrowserView* browser_view = BrowserView::GetBrowserViewForBrowser(browser());
+  aura::Window* browser_window = browser_view->GetWidget()->GetNativeWindow();
+  gfx::Rect browser_screen_bounds = browser_window->GetBoundsInScreen();
+
+  const gfx::Point start_position(
+      browser_screen_bounds.CenterPoint().x(),
+      browser_screen_bounds.y() + browser_view->GetTabStripHeight() / 2);
+
+  test::WaitForNoPointerHoldLock();
+  ASSERT_TRUE(
+      ui_test_utils::SendMouseMoveSync(start_position) &&
+      ui_test_utils::SendMouseEventsSync(ui_controls::LEFT, ui_controls::DOWN));
+
+  // One more mouse move to start drag.
+  ASSERT_TRUE(ui_test_utils::SendMouseMoveSync(start_position));
+
+  // Drag 25% of the work area height.
+  const int drag_length = display::Screen::GetScreen()
+                              ->GetDisplayNearestWindow(browser_window)
+                              .work_area_size()
+                              .height() /
+                          4;
+  constexpr int kSteps = 20;
+  gfx::Vector2d delta(0, drag_length / kSteps);
+  ContinueDrag(start_position, delta, kSteps);
+  EXPECT_TRUE(IsOverviewSelecting());
+}
+
+IN_PROC_BROWSER_TEST_F(DragToOverviewTest, DragTab) {
+  BrowserView* browser_view = BrowserView::GetBrowserViewForBrowser(browser());
+  aura::Window* browser_window = browser_view->GetWidget()->GetNativeWindow();
+
+  AddBlankTabAndShow(browser());
+  browser_view->tabstrip()->StopAnimating(true);
+
+  gfx::Point drag_position(ui_test_utils::GetCenterInScreenCoordinates(
+      browser_view->tabstrip()->tab_at(0)));
+
+  test::WaitForNoPointerHoldLock();
+  ASSERT_TRUE(
+      ui_test_utils::SendMouseMoveSync(drag_position) &&
+      ui_test_utils::SendMouseEventsSync(ui_controls::LEFT, ui_controls::DOWN));
+
+  // Drag 25% of the work area height.
+  const int drag_length = display::Screen::GetScreen()
+                              ->GetDisplayNearestWindow(browser_window)
+                              .work_area_size()
+                              .height() /
+                          4;
+  constexpr int kSteps = 20;
+  gfx::Vector2d delta(0, drag_length / kSteps);
+
+  // Drag tab far enough to detach.
+  drag_position.Offset(0, GetDetachY(browser_view->tabstrip()));
+  ui_controls::SendMouseMoveNotifyWhenDone(
+      drag_position.x(), drag_position.y(),
+      base::BindOnce(&DragToOverviewTest::VerifyTabDetachedAndContinueDrag,
+                     base::Unretained(this), drag_position, delta, kSteps));
+
+  // Wait for the drag to finish.
+  content::WindowedNotificationObserver(
+      chrome::NOTIFICATION_TAB_DRAG_LOOP_DONE,
+      content::NotificationService::AllSources())
+      .Wait();
+}
diff --git a/chrome/browser/ui/ash/session_controller_client.cc b/chrome/browser/ui/ash/session_controller_client.cc
index f314c433..18aabb9d 100644
--- a/chrome/browser/ui/ash/session_controller_client.cc
+++ b/chrome/browser/ui/ash/session_controller_client.cc
@@ -26,6 +26,8 @@
 #include "chrome/browser/chromeos/profiles/profile_helper.h"
 #include "chrome/browser/chromeos/settings/device_settings_service.h"
 #include "chrome/browser/lifetime/application_lifetime.h"
+#include "chrome/browser/policy/profile_policy_connector.h"
+#include "chrome/browser/policy/profile_policy_connector_factory.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/profiles/profile_manager.h"
 #include "chrome/browser/profiles/profiles_state.h"
@@ -100,6 +102,18 @@
   session->user_info->is_ephemeral =
       UserManager::Get()->IsUserNonCryptohomeDataEphemeral(user.GetAccountId());
   session->user_info->has_gaia_account = user.has_gaia_account();
+  if (user.GetType() == user_manager::USER_TYPE_PUBLIC_ACCOUNT ||
+      user.GetType() == user_manager::USER_TYPE_KIOSK_APP ||
+      user.GetType() == user_manager::USER_TYPE_ARC_KIOSK_APP) {
+    session->user_info->is_managed = true;
+  } else {
+    session->user_info->is_managed =
+        (profile &&
+         policy::ProfilePolicyConnectorFactory::GetForBrowserContext(profile) !=
+             nullptr &&
+         policy::ProfilePolicyConnectorFactory::GetForBrowserContext(profile)
+             ->IsManaged());
+  }
   const AccountId& owner_id = UserManager::Get()->GetOwnerAccountId();
   session->user_info->is_device_owner =
       owner_id.is_valid() && owner_id == user.GetAccountId();
diff --git a/chrome/browser/ui/ash/split_view_interactive_uitest.cc b/chrome/browser/ui/ash/split_view_interactive_uitest.cc
index 0b215b9..311581b 100644
--- a/chrome/browser/ui/ash/split_view_interactive_uitest.cc
+++ b/chrome/browser/ui/ash/split_view_interactive_uitest.cc
@@ -3,9 +3,6 @@
 // found in the LICENSE file.
 
 #include "ash/public/cpp/ash_switches.h"
-#include "ash/public/interfaces/constants.mojom.h"
-#include "ash/public/interfaces/shell_test_api.test-mojom-test-utils.h"
-#include "ash/public/interfaces/shell_test_api.test-mojom.h"
 #include "ash/shell.h"                               // mash-ok
 #include "ash/wm/splitview/split_view_controller.h"  // mash-ok
 #include "base/macros.h"
@@ -13,17 +10,15 @@
 #include "base/system/sys_info.h"
 #include "base/task/post_task.h"
 #include "base/test/bind_test_util.h"
+#include "chrome/browser/ui/ash/ash_test_util.h"
 #include "chrome/browser/ui/ash/tablet_mode_client_test_util.h"
 #include "chrome/browser/ui/browser.h"
 #include "chrome/browser/ui/browser_window.h"
 #include "chrome/browser/ui/views/frame/browser_view.h"
 #include "chrome/test/base/interactive_test_utils.h"
 #include "chrome/test/base/perf/performance_test.h"
-#include "content/public/common/service_manager_connection.h"
 #include "content/public/common/service_names.mojom.h"
-#include "services/service_manager/public/cpp/connector.h"
 #include "ui/aura/mus/window_mus.h"
-#include "ui/aura/test/mus/change_completion_waiter.h"
 #include "ui/aura/window.h"
 #include "ui/aura/window_tree_host.h"
 #include "ui/base/ui_base_features.h"
@@ -48,11 +43,8 @@
   SplitViewTest() = default;
   ~SplitViewTest() override = default;
 
-  // InProcessBrowserTest:
+  // UIPerformanceTest:
   void SetUpCommandLine(base::CommandLine* command_line) override {
-    // Make sure the test actually draws to screen and uses the real gpu.
-    command_line->AppendSwitch(switches::kEnablePixelOutputInTests);
-    command_line->AppendSwitch(switches::kUseGpuInTests);
     command_line->AppendSwitch(ash::switches::kAshEnableTabletMode);
   }
 
@@ -64,16 +56,6 @@
   DISALLOW_COPY_AND_ASSIGN(SplitViewTest);
 };
 
-void WaitForNoPointerHoldLock() {
-  ash::mojom::ShellTestApiPtr shell_test_api;
-  content::ServiceManagerConnection::GetForProcess()
-      ->GetConnector()
-      ->BindInterface(ash::mojom::kServiceName, &shell_test_api);
-  ash::mojom::ShellTestApiAsyncWaiter waiter(shell_test_api.get());
-  waiter.WaitForNoPointerHoldLock();
-  aura::test::WaitForAllChangesToComplete();
-}
-
 // Used to wait for a window resize to show up on screen.
 class WidgetResizeWaiter : public views::WidgetObserver {
  public:
@@ -87,12 +69,14 @@
   }
 
   void WaitForDisplay() {
-    if (waiting_for_frame_) {
-      run_loop_ = std::make_unique<base::RunLoop>();
-      run_loop_->Run();
-      EXPECT_FALSE(waiting_for_frame_);
-    }
-    WaitForNoPointerHoldLock();
+    do {
+      if (waiting_for_frame_) {
+        run_loop_ = std::make_unique<base::RunLoop>();
+        run_loop_->Run();
+        EXPECT_FALSE(waiting_for_frame_);
+      }
+      test::WaitForNoPointerHoldLock();
+    } while (waiting_for_frame_);
   }
 
  private:
@@ -150,10 +134,7 @@
   views::Widget* browser2_widget =
       BrowserView::GetBrowserViewForBrowser(browser2)->GetWidget();
   if (features::IsUsingWindowService()) {
-    ash::mojom::ShellTestApiPtr shell_test_api;
-    content::ServiceManagerConnection::GetForProcess()
-        ->GetConnector()
-        ->BindInterface(ash::mojom::kServiceName, &shell_test_api);
+    ash::mojom::ShellTestApiPtr shell_test_api = test::GetShellTestApi();
 
     {
       base::RunLoop run_loop;
@@ -180,7 +161,7 @@
     shell->split_view_controller()->FlushForTesting();
   }
 
-  WaitForNoPointerHoldLock();
+  test::WaitForNoPointerHoldLock();
 
   const gfx::Size display_size =
       display::Screen::GetScreen()->GetPrimaryDisplay().bounds().size();
diff --git a/chrome/browser/ui/views/payments/payment_request_item_list.cc b/chrome/browser/ui/views/payments/payment_request_item_list.cc
index b42e692..c0db5bf2c 100644
--- a/chrome/browser/ui/views/payments/payment_request_item_list.cc
+++ b/chrome/browser/ui/views/payments/payment_request_item_list.cc
@@ -124,14 +124,11 @@
 void PaymentRequestItemList::Item::SetSelected(bool selected, bool notify) {
   selected_ = selected;
 
-  (void)std::find_if(
-      children().cbegin(), children().cend(), [selected](views::View* child) {
-        bool found =
-            child->id() == static_cast<int>(DialogViewID::CHECKMARK_VIEW);
-        if (found)
-          child->SetVisible(selected);
-        return found;
-      });
+  for (views::View* child : children())
+    if (child->id() == static_cast<int>(DialogViewID::CHECKMARK_VIEW)) {
+      child->SetVisible(selected);
+      break;
+    }
 
   UpdateAccessibleName();
 
diff --git a/chrome/browser/ui/views/webauthn/sheet_view_factory.cc b/chrome/browser/ui/views/webauthn/sheet_view_factory.cc
index a3bc4c2..16f94cff 100644
--- a/chrome/browser/ui/views/webauthn/sheet_view_factory.cc
+++ b/chrome/browser/ui/views/webauthn/sheet_view_factory.cc
@@ -171,6 +171,11 @@
       sheet_view = std::make_unique<AuthenticatorSelectAccountSheetView>(
           std::make_unique<AuthenticatorSelectAccountSheetModel>(dialog_model));
       break;
+    case Step::kAttestationPermissionRequest:
+      sheet_view = std::make_unique<AuthenticatorRequestSheetView>(
+          std::make_unique<AttestationPermissionRequestSheetModel>(
+              dialog_model));
+      break;
     case Step::kNotStarted:
     case Step::kClosed:
       sheet_view = std::make_unique<AuthenticatorRequestSheetView>(
diff --git a/chrome/browser/ui/webauthn/authenticator_dialog_browsertest.cc b/chrome/browser/ui/webauthn/authenticator_dialog_browsertest.cc
index eafef88..4ecd52e 100644
--- a/chrome/browser/ui/webauthn/authenticator_dialog_browsertest.cc
+++ b/chrome/browser/ui/webauthn/authenticator_dialog_browsertest.cc
@@ -145,6 +145,8 @@
       model->SelectAccount(
           std::move(responses),
           base::Bind([](device::AuthenticatorGetAssertionResponse) {}));
+    } else if (name == "request_attestation_permission") {
+      model->RequestAttestationPermission(base::DoNothing());
     }
 
     ShowAuthenticatorRequestDialog(
@@ -279,3 +281,8 @@
 IN_PROC_BROWSER_TEST_F(AuthenticatorDialogTest, InvokeUi_account_select) {
   ShowAndVerifyUi();
 }
+
+IN_PROC_BROWSER_TEST_F(AuthenticatorDialogTest,
+                       InvokeUi_request_attestation_permission) {
+  ShowAndVerifyUi();
+}
diff --git a/chrome/browser/ui/webauthn/sheet_models.cc b/chrome/browser/ui/webauthn/sheet_models.cc
index c7561620..e4b377b 100644
--- a/chrome/browser/ui/webauthn/sheet_models.cc
+++ b/chrome/browser/ui/webauthn/sheet_models.cc
@@ -1030,3 +1030,54 @@
 
 void AuthenticatorSelectAccountSheetModel::SetObserver(
     ui::TableModelObserver* observer) {}
+
+// AttestationPermissionRequestSheetModel -------------------------------------
+
+AttestationPermissionRequestSheetModel::AttestationPermissionRequestSheetModel(
+    AuthenticatorRequestDialogModel* dialog_model)
+    : AuthenticatorSheetModelBase(dialog_model) {}
+
+AttestationPermissionRequestSheetModel::
+    ~AttestationPermissionRequestSheetModel() = default;
+
+void AttestationPermissionRequestSheetModel::OnAccept() {
+  dialog_model()->OnAttestationPermissionResponse(true);
+}
+
+void AttestationPermissionRequestSheetModel::OnCancel() {
+  dialog_model()->OnAttestationPermissionResponse(false);
+}
+
+gfx::ImageSkia* AttestationPermissionRequestSheetModel::GetStepIllustration()
+    const {
+  return GetImage(IDR_WEBAUTHN_ILLUSTRATION_PERMISSION);
+}
+
+base::string16 AttestationPermissionRequestSheetModel::GetStepTitle() const {
+  return l10n_util::GetStringUTF16(
+      IDS_WEBAUTHN_REQUEST_ATTESTATION_PERMISSION_TITLE);
+}
+
+base::string16 AttestationPermissionRequestSheetModel::GetStepDescription()
+    const {
+  return l10n_util::GetStringFUTF16(
+      IDS_WEBAUTHN_REQUEST_ATTESTATION_PERMISSION_DESC,
+      GetRelyingPartyIdString());
+}
+
+bool AttestationPermissionRequestSheetModel::IsAcceptButtonVisible() const {
+  return true;
+}
+
+bool AttestationPermissionRequestSheetModel::IsAcceptButtonEnabled() const {
+  return true;
+}
+
+base::string16 AttestationPermissionRequestSheetModel::GetAcceptButtonLabel()
+    const {
+  return l10n_util::GetStringUTF16(IDS_WEBAUTHN_ALLOW_ATTESTATION);
+}
+
+bool AttestationPermissionRequestSheetModel::IsCancelButtonVisible() const {
+  return true;
+}
diff --git a/chrome/browser/ui/webauthn/sheet_models.h b/chrome/browser/ui/webauthn/sheet_models.h
index 7eae1cb..b1419e4 100644
--- a/chrome/browser/ui/webauthn/sheet_models.h
+++ b/chrome/browser/ui/webauthn/sheet_models.h
@@ -486,4 +486,25 @@
   size_t selected_ = 0;
 };
 
+class AttestationPermissionRequestSheetModel
+    : public AuthenticatorSheetModelBase {
+ public:
+  explicit AttestationPermissionRequestSheetModel(
+      AuthenticatorRequestDialogModel* dialog_model);
+  ~AttestationPermissionRequestSheetModel() override;
+
+  // AuthenticatorSheetModelBase:
+  void OnAccept() override;
+  void OnCancel() override;
+
+ private:
+  // AuthenticatorSheetModelBase:
+  gfx::ImageSkia* GetStepIllustration() const override;
+  base::string16 GetStepTitle() const override;
+  base::string16 GetStepDescription() const override;
+  bool IsAcceptButtonVisible() const override;
+  bool IsAcceptButtonEnabled() const override;
+  base::string16 GetAcceptButtonLabel() const override;
+  bool IsCancelButtonVisible() const override;
+};
 #endif  // CHROME_BROWSER_UI_WEBAUTHN_SHEET_MODELS_H_
diff --git a/chrome/browser/ui/webui/sync_internals_message_handler.cc b/chrome/browser/ui/webui/sync_internals_message_handler.cc
index b7e652cf..3f5b7c97 100644
--- a/chrome/browser/ui/webui/sync_internals_message_handler.cc
+++ b/chrome/browser/ui/webui/sync_internals_message_handler.cc
@@ -9,7 +9,6 @@
 
 #include "base/bind.h"
 #include "base/command_line.h"
-#include "base/feature_list.h"
 #include "base/logging.h"
 #include "base/strings/string_number_conversions.h"
 #include "chrome/browser/profiles/profile.h"
@@ -263,9 +262,10 @@
     const base::ListValue* args) {
   DCHECK(args->empty());
   AllowJavascript();
-  CallJavascriptFunction(
-      syncer::sync_ui_util::kUserEventsVisibilityCallback,
-      Value(base::FeatureList::IsEnabled(switches::kSyncUserEvents)));
+  // TODO(crbug.com/934333): Get rid of this callback now that user events are
+  // always enabled.
+  CallJavascriptFunction(syncer::sync_ui_util::kUserEventsVisibilityCallback,
+                         Value(true));
 }
 
 void SyncInternalsMessageHandler::HandleSetIncludeSpecifics(
diff --git a/chrome/browser/web_applications/bookmark_apps/policy/web_app_policy_manager_unittest.cc b/chrome/browser/web_applications/bookmark_apps/policy/web_app_policy_manager_unittest.cc
index ff0156f..aa7be133 100644
--- a/chrome/browser/web_applications/bookmark_apps/policy/web_app_policy_manager_unittest.cc
+++ b/chrome/browser/web_applications/bookmark_apps/policy/web_app_policy_manager_unittest.cc
@@ -12,6 +12,7 @@
 #include "base/values.h"
 #include "chrome/browser/prefs/browser_prefs.h"
 #include "chrome/browser/web_applications/bookmark_apps/test_web_app_provider.h"
+#include "chrome/browser/web_applications/components/install_options.h"
 #include "chrome/browser/web_applications/components/pending_app_manager.h"
 #include "chrome/browser/web_applications/components/policy/web_app_policy_constants.h"
 #include "chrome/browser/web_applications/components/test_pending_app_manager.h"
@@ -35,9 +36,9 @@
 
 namespace {
 
-const char kWindowedUrl[] = "https://windowed.example";
-const char kTabbedUrl[] = "https://tabbed.example";
-const char kNoContainerUrl[] = "https://no-container.example";
+const char kWindowedUrl[] = "https://windowed.example/";
+const char kTabbedUrl[] = "https://tabbed.example/";
+const char kNoContainerUrl[] = "https://no-container.example/";
 
 base::Value GetWindowedItem() {
   base::Value item(base::Value::Type::DICTIONARY);
@@ -161,7 +162,7 @@
     return provider;
   }
 
-  std::string GenerateFakeExtensionId(GURL& url) {
+  std::string GenerateFakeExtensionId(const GURL& url) {
     return crx_file::id_util::GenerateId("fake_app_id_for:" + url.spec());
   }
 
@@ -383,4 +384,52 @@
             pending_app_manager()->uninstall_requests());
 }
 
+// Tests that we correctly reinstall a placeholder app.
+TEST_F(WebAppPolicyManagerTest, ReinstallPlaceholderApp) {
+  base::Value list(base::Value::Type::LIST);
+  list.GetList().push_back(GetWindowedItem());
+  profile()->GetPrefs()->Set(prefs::kWebAppInstallForceList, std::move(list));
+
+  policy_manager()->Start();
+  base::RunLoop().RunUntilIdle();
+
+  std::vector<InstallOptions> expected_options_list;
+  expected_options_list.push_back(GetWindowedInstallOptions());
+
+  const auto& install_options_list = pending_app_manager()->install_requests();
+  EXPECT_EQ(expected_options_list, install_options_list);
+
+  policy_manager()->ReinstallPlaceholderAppIfNecessary(GURL(kWindowedUrl));
+  base::RunLoop().RunUntilIdle();
+
+  EXPECT_EQ(expected_options_list, install_options_list);
+
+  const auto& reinstall_options_list =
+      pending_app_manager()->reinstall_requests();
+  EXPECT_EQ(expected_options_list, reinstall_options_list);
+}
+
+TEST_F(WebAppPolicyManagerTest, TryToInexistentPlaceholderApp) {
+  base::Value list(base::Value::Type::LIST);
+  list.GetList().push_back(GetWindowedItem());
+  profile()->GetPrefs()->Set(prefs::kWebAppInstallForceList, std::move(list));
+
+  policy_manager()->Start();
+  base::RunLoop().RunUntilIdle();
+
+  std::vector<InstallOptions> expected_options_list;
+  expected_options_list.push_back(GetWindowedInstallOptions());
+
+  const auto& install_options_list = pending_app_manager()->install_requests();
+  EXPECT_EQ(expected_options_list, install_options_list);
+  EXPECT_TRUE(pending_app_manager()->reinstall_requests().empty());
+
+  // Try to reinstall for app not installed by policy.
+  policy_manager()->ReinstallPlaceholderAppIfNecessary(GURL(kTabbedUrl));
+  base::RunLoop().RunUntilIdle();
+
+  EXPECT_EQ(expected_options_list, install_options_list);
+  EXPECT_TRUE(pending_app_manager()->reinstall_requests().empty());
+}
+
 }  // namespace web_app
diff --git a/chrome/browser/web_applications/components/pending_app_manager.h b/chrome/browser/web_applications/components/pending_app_manager.h
index f423a3a..1b4c158a 100644
--- a/chrome/browser/web_applications/components/pending_app_manager.h
+++ b/chrome/browser/web_applications/components/pending_app_manager.h
@@ -74,6 +74,14 @@
   virtual void UninstallApps(std::vector<GURL> uninstall_urls,
                              const UninstallCallback& callback) = 0;
 
+  // If there is a placeholder app for |install_options.url| removes that app
+  // and tries to install the app again.
+  // TODO(ortuno): Temporarily use the same callback as Install. We should
+  // figure out if we want a separate enum for reinstall results or just re-use
+  // the InstallResult enum for both methods.
+  virtual void ReinstallPlaceholderApp(InstallOptions install_options,
+                                       OnceInstallCallback callback) = 0;
+
   // Returns the URLs of those apps installed from |install_source|.
   virtual std::vector<GURL> GetInstalledAppUrls(
       InstallSource install_source) const = 0;
diff --git a/chrome/browser/web_applications/components/policy/web_app_policy_manager.cc b/chrome/browser/web_applications/components/policy/web_app_policy_manager.cc
index 09df440c..77100ad 100644
--- a/chrome/browser/web_applications/components/policy/web_app_policy_manager.cc
+++ b/chrome/browser/web_applications/components/policy/web_app_policy_manager.cc
@@ -4,13 +4,16 @@
 
 #include "chrome/browser/web_applications/components/policy/web_app_policy_manager.h"
 
+#include <algorithm>
 #include <utility>
 #include <vector>
 
 #include "base/bind.h"
+#include "base/bind_helpers.h"
 #include "base/task/post_task.h"
 #include "base/values.h"
 #include "chrome/browser/profiles/profile.h"
+#include "chrome/browser/web_applications/components/install_options.h"
 #include "chrome/browser/web_applications/components/policy/web_app_policy_constants.h"
 #include "chrome/browser/web_applications/components/web_app_constants.h"
 #include "chrome/common/pref_names.h"
@@ -21,6 +24,51 @@
 
 namespace web_app {
 
+namespace {
+
+InstallOptions GetInstallOptionsForPolicyEntry(const base::Value& entry) {
+  const base::Value& url = *entry.FindKey(kUrlKey);
+  const base::Value* default_launch_container =
+      entry.FindKey(kDefaultLaunchContainerKey);
+  const base::Value* create_desktop_shortcut =
+      entry.FindKey(kCreateDesktopShorcutKey);
+
+  DCHECK(!default_launch_container ||
+         default_launch_container->GetString() ==
+             kDefaultLaunchContainerWindowValue ||
+         default_launch_container->GetString() ==
+             kDefaultLaunchContainerTabValue);
+
+  LaunchContainer launch_container;
+  if (!default_launch_container) {
+    launch_container = LaunchContainer::kTab;
+  } else if (default_launch_container->GetString() ==
+             kDefaultLaunchContainerTabValue) {
+    launch_container = LaunchContainer::kTab;
+  } else {
+    launch_container = LaunchContainer::kWindow;
+  }
+
+  InstallOptions install_options(GURL(url.GetString()), launch_container,
+                                 web_app::InstallSource::kExternalPolicy);
+
+  bool create_shortcut = false;
+  if (create_desktop_shortcut)
+    create_shortcut = create_desktop_shortcut->GetBool();
+
+  install_options.add_to_applications_menu = create_shortcut;
+  install_options.add_to_desktop = create_shortcut;
+
+  // It's not yet clear how pinning to shelf will work for policy installed
+  // Web Apps, but for now never pin them. See crbug.com/880125.
+  install_options.add_to_quick_launch_bar = false;
+
+  install_options.install_placeholder = true;
+
+  return install_options;
+}
+
+}  // namespace
 WebAppPolicyManager::WebAppPolicyManager(Profile* profile,
                                          PendingAppManager* pending_app_manager)
     : profile_(profile),
@@ -39,6 +87,26 @@
                      weak_ptr_factory_.GetWeakPtr()));
 }
 
+void WebAppPolicyManager::ReinstallPlaceholderAppIfNecessary(const GURL& url) {
+  const base::Value* web_apps =
+      pref_service_->GetList(prefs::kWebAppInstallForceList);
+  const auto& web_apps_list = web_apps->GetList();
+
+  const auto it =
+      std::find_if(web_apps_list.begin(), web_apps_list.end(),
+                   [&url](const base::Value& entry) {
+                     return entry.FindKey(kUrlKey)->GetString() == url.spec();
+                   });
+
+  if (it == web_apps_list.end())
+    return;
+
+  // If the app is not a placeholder app, PendingAppManager will ignore the
+  // request.
+  pending_app_manager_->ReinstallPlaceholderApp(
+      GetInstallOptionsForPolicyEntry(*it), base::DoNothing());
+}
+
 // static
 void WebAppPolicyManager::RegisterProfilePrefs(
     user_prefs::PrefRegistrySyncable* registry) {
@@ -62,46 +130,8 @@
   // No need to validate the types or values of the policy members because we
   // are using a SimpleSchemaValidatingPolicyHandler which should validate them
   // for us.
-  for (const base::Value& info : web_apps->GetList()) {
-    const base::Value& url = *info.FindKey(kUrlKey);
-    const base::Value* default_launch_container =
-        info.FindKey(kDefaultLaunchContainerKey);
-    const base::Value* create_desktop_shortcut =
-        info.FindKey(kCreateDesktopShorcutKey);
-
-    DCHECK(!default_launch_container ||
-           default_launch_container->GetString() ==
-               kDefaultLaunchContainerWindowValue ||
-           default_launch_container->GetString() ==
-               kDefaultLaunchContainerTabValue);
-
-    LaunchContainer launch_container;
-    if (!default_launch_container)
-      launch_container = LaunchContainer::kTab;
-    else if (default_launch_container->GetString() ==
-             kDefaultLaunchContainerTabValue)
-      launch_container = LaunchContainer::kTab;
-    else
-      launch_container = LaunchContainer::kWindow;
-
-    InstallOptions install_options(GURL(std::move(url.GetString())),
-                                   launch_container,
-                                   web_app::InstallSource::kExternalPolicy);
-
-    bool create_shortcut = false;
-    if (create_desktop_shortcut)
-      create_shortcut = create_desktop_shortcut->GetBool();
-
-    install_options.add_to_applications_menu = create_shortcut;
-    install_options.add_to_desktop = create_shortcut;
-
-    // It's not yet clear how pinning to shelf will work for policy installed
-    // Web Apps, but for now never pin them. See crbug.com/880125.
-    install_options.add_to_quick_launch_bar = false;
-
-    install_options.install_placeholder = true;
-
-    install_options_list.push_back(std::move(install_options));
+  for (const base::Value& entry : web_apps->GetList()) {
+    install_options_list.push_back(GetInstallOptionsForPolicyEntry(entry));
   }
 
   pending_app_manager_->SynchronizeInstalledApps(
diff --git a/chrome/browser/web_applications/components/policy/web_app_policy_manager.h b/chrome/browser/web_applications/components/policy/web_app_policy_manager.h
index c931296..5bb4cfd 100644
--- a/chrome/browser/web_applications/components/policy/web_app_policy_manager.h
+++ b/chrome/browser/web_applications/components/policy/web_app_policy_manager.h
@@ -35,6 +35,8 @@
 
   void Start();
 
+  void ReinstallPlaceholderAppIfNecessary(const GURL& url);
+
   static void RegisterProfilePrefs(user_prefs::PrefRegistrySyncable* registry);
 
  private:
diff --git a/chrome/browser/web_applications/components/test_pending_app_manager.cc b/chrome/browser/web_applications/components/test_pending_app_manager.cc
index 17e348c8..b05481c 100644
--- a/chrome/browser/web_applications/components/test_pending_app_manager.cc
+++ b/chrome/browser/web_applications/components/test_pending_app_manager.cc
@@ -72,6 +72,14 @@
   }
 }
 
+void TestPendingAppManager::ReinstallPlaceholderApp(
+    InstallOptions install_options,
+    OnceInstallCallback callback) {
+  reinstall_requests_.push_back(std::move(install_options));
+  std::move(callback).Run(reinstall_requests_.back().url,
+                          InstallResultCode::kSuccess);
+}
+
 std::vector<GURL> TestPendingAppManager::GetInstalledAppUrls(
     InstallSource install_source) const {
   std::vector<GURL> urls;
diff --git a/chrome/browser/web_applications/components/test_pending_app_manager.h b/chrome/browser/web_applications/components/test_pending_app_manager.h
index 2501df37..69ebab7 100644
--- a/chrome/browser/web_applications/components/test_pending_app_manager.h
+++ b/chrome/browser/web_applications/components/test_pending_app_manager.h
@@ -30,6 +30,9 @@
   const std::vector<GURL>& uninstall_requests() const {
     return uninstall_requests_;
   }
+  const std::vector<InstallOptions>& reinstall_requests() const {
+    return reinstall_requests_;
+  }
 
   int deduped_install_count() const { return deduped_install_count_; }
   int deduped_uninstall_count() const { return deduped_uninstall_count_; }
@@ -49,6 +52,8 @@
                    const RepeatingInstallCallback& callback) override;
   void UninstallApps(std::vector<GURL> uninstall_urls,
                      const UninstallCallback& callback) override;
+  void ReinstallPlaceholderApp(InstallOptions install_options,
+                               OnceInstallCallback callback) override;
   std::vector<GURL> GetInstalledAppUrls(
       InstallSource install_source) const override;
   base::Optional<std::string> LookupAppId(const GURL& url) const override;
@@ -57,6 +62,7 @@
   void DoInstall(InstallOptions install_options, OnceInstallCallback callback);
   std::vector<InstallOptions> install_requests_;
   std::vector<GURL> uninstall_requests_;
+  std::vector<InstallOptions> reinstall_requests_;
 
   int deduped_install_count_;
   int deduped_uninstall_count_;
diff --git a/chrome/browser/web_applications/components/web_app_tab_helper_base.cc b/chrome/browser/web_applications/components/web_app_tab_helper_base.cc
index 23a6f57..6b471583 100644
--- a/chrome/browser/web_applications/components/web_app_tab_helper_base.cc
+++ b/chrome/browser/web_applications/components/web_app_tab_helper_base.cc
@@ -5,7 +5,10 @@
 #include "chrome/browser/web_applications/components/web_app_tab_helper_base.h"
 
 #include "base/unguessable_token.h"
+#include "chrome/browser/profiles/profile.h"
+#include "chrome/browser/web_applications/components/policy/web_app_policy_manager.h"
 #include "chrome/browser/web_applications/components/web_app_audio_focus_id_map.h"
+#include "chrome/browser/web_applications/components/web_app_provider_base.h"
 #include "content/public/browser/media_session.h"
 #include "content/public/browser/navigation_handle.h"
 #include "content/public/browser/site_instance.h"
@@ -48,6 +51,8 @@
 
   const AppId app_id = FindAppIdInScopeOfUrl(navigation_handle->GetURL());
   SetAppId(app_id);
+
+  ReinstallPlaceholderAppIfNecessary(navigation_handle->GetURL());
 }
 
 void WebAppTabHelperBase::DidCloneToNewWebContents(
@@ -98,4 +103,16 @@
       ->SetAudioFocusGroupId(audio_focus_group_id_);
 }
 
+void WebAppTabHelperBase::ReinstallPlaceholderAppIfNecessary(const GURL& url) {
+  auto* provider = web_app::WebAppProviderBase::GetProviderBase(
+      Profile::FromBrowserContext(web_contents()->GetBrowserContext()));
+  DCHECK(provider);
+
+  // WebAppPolicyManager might be nullptr in the non-extensions implementation.
+  if (!provider->policy_manager())
+    return;
+
+  provider->policy_manager()->ReinstallPlaceholderAppIfNecessary(url);
+}
+
 }  // namespace web_app
diff --git a/chrome/browser/web_applications/components/web_app_tab_helper_base.h b/chrome/browser/web_applications/components/web_app_tab_helper_base.h
index 088ae80..124b4c76 100644
--- a/chrome/browser/web_applications/components/web_app_tab_helper_base.h
+++ b/chrome/browser/web_applications/components/web_app_tab_helper_base.h
@@ -87,6 +87,9 @@
   // Updates the audio focus group id based on the current web app.
   void UpdateAudioFocusGroupId();
 
+  // Triggers a reinstall of a placeholder app for |url|.
+  void ReinstallPlaceholderAppIfNecessary(const GURL& url);
+
   // WebApp associated with this tab. Empty string if no app associated.
   AppId app_id_;
 
diff --git a/chrome/browser/web_applications/extensions/pending_bookmark_app_manager.cc b/chrome/browser/web_applications/extensions/pending_bookmark_app_manager.cc
index 011f099..4480c40 100644
--- a/chrome/browser/web_applications/extensions/pending_bookmark_app_manager.cc
+++ b/chrome/browser/web_applications/extensions/pending_bookmark_app_manager.cc
@@ -93,6 +93,38 @@
   }
 }
 
+void PendingBookmarkAppManager::ReinstallPlaceholderApp(
+    web_app::InstallOptions install_options,
+    OnceInstallCallback callback) {
+  base::ThreadTaskRunnerHandle::Get()->PostTask(
+      FROM_HERE,
+      base::BindOnce(&PendingBookmarkAppManager::StartReinstallTask,
+                     weak_ptr_factory_.GetWeakPtr(), std::move(install_options),
+                     std::move(callback)));
+}
+
+void PendingBookmarkAppManager::StartReinstallTask(
+    web_app::InstallOptions install_options,
+    OnceInstallCallback callback) {
+  base::Optional<std::string> extension_id =
+      extension_ids_map_.LookupPlaceholderAppId(install_options.url);
+
+  bool uninstall_succeeded = true;
+  if (extension_id.has_value() &&
+      registrar_->IsInstalled(extension_id.value())) {
+    uninstall_succeeded = uninstaller_->UninstallApp(install_options.url);
+  }
+
+  if (!uninstall_succeeded) {
+    LOG(WARNING) << "Could not uninstall Web App for : " << install_options.url;
+    std::move(callback).Run(install_options.url,
+                            web_app::InstallResultCode::kFailedUnknownReason);
+    return;
+  }
+
+  Install(std::move(install_options), std::move(callback));
+}
+
 std::vector<GURL> PendingBookmarkAppManager::GetInstalledAppUrls(
     web_app::InstallSource install_source) const {
   return web_app::ExtensionIdsMap::GetInstalledAppUrls(profile_,
diff --git a/chrome/browser/web_applications/extensions/pending_bookmark_app_manager.h b/chrome/browser/web_applications/extensions/pending_bookmark_app_manager.h
index 614309f..f9637e08 100644
--- a/chrome/browser/web_applications/extensions/pending_bookmark_app_manager.h
+++ b/chrome/browser/web_applications/extensions/pending_bookmark_app_manager.h
@@ -63,6 +63,8 @@
                    const RepeatingInstallCallback& callback) override;
   void UninstallApps(std::vector<GURL> uninstall_urls,
                      const UninstallCallback& callback) override;
+  void ReinstallPlaceholderApp(web_app::InstallOptions install_options,
+                               OnceInstallCallback callback) override;
   std::vector<GURL> GetInstalledAppUrls(
       web_app::InstallSource install_source) const override;
   base::Optional<std::string> LookupAppId(const GURL& url) const override;
@@ -80,6 +82,9 @@
 
   void StartInstallationTask(std::unique_ptr<TaskAndCallback> task);
 
+  void StartReinstallTask(web_app::InstallOptions install_options,
+                          OnceInstallCallback callback);
+
   void CreateWebContentsIfNecessary();
 
   void OnUrlLoaded(web_app::WebAppUrlLoader::Result result);
diff --git a/chrome/browser/web_applications/extensions/pending_bookmark_app_manager_unittest.cc b/chrome/browser/web_applications/extensions/pending_bookmark_app_manager_unittest.cc
index cbaf4a1..01093db9 100644
--- a/chrome/browser/web_applications/extensions/pending_bookmark_app_manager_unittest.cc
+++ b/chrome/browser/web_applications/extensions/pending_bookmark_app_manager_unittest.cc
@@ -105,7 +105,7 @@
       BookmarkAppInstallationTask::ResultCallback callback) override {
     std::move(on_install_placeholder_called_).Run();
     if (succeeds_) {
-      std::move(callback).Run(SimulateInstallingApp());
+      std::move(callback).Run(SimulateInstallingApp(true /* is_placeholder */));
     } else {
       std::move(callback).Run(BookmarkAppInstallationTask::Result(
           web_app::InstallResultCode::kFailedUnknownReason, std::string()));
@@ -122,10 +122,12 @@
   }
 
  private:
-  BookmarkAppInstallationTask::Result SimulateInstallingApp() {
+  BookmarkAppInstallationTask::Result SimulateInstallingApp(
+      bool is_placeholder = false) {
     std::string app_id = GenerateFakeAppId(install_options().url);
     extension_ids_map_.Insert(install_options().url, app_id,
                               install_options().install_source);
+    extension_ids_map_.SetIsPlaceholder(install_options().url, is_placeholder);
     registrar_->AddAsInstalled(app_id);
     return {web_app::InstallResultCode::kSuccess, app_id};
   }
@@ -143,8 +145,9 @@
 
 class TestBookmarkAppUninstaller : public BookmarkAppUninstaller {
  public:
-  TestBookmarkAppUninstaller(Profile* profile, web_app::AppRegistrar* registrar)
-      : BookmarkAppUninstaller(profile, registrar) {}
+  TestBookmarkAppUninstaller(Profile* profile,
+                             web_app::TestAppRegistrar* registrar)
+      : BookmarkAppUninstaller(profile, registrar), registrar_(registrar) {}
 
   ~TestBookmarkAppUninstaller() override = default;
 
@@ -170,11 +173,16 @@
 
     bool result = next_result_map_[app_url];
     next_result_map_.erase(app_url);
+
+    if (result)
+      registrar_->RemoveAsInstalled(GenerateFakeAppId(app_url));
+
     return result;
   }
 
  private:
   std::map<GURL, bool> next_result_map_;
+  web_app::TestAppRegistrar* registrar_;
 
   size_t uninstall_call_count_ = 0;
   std::vector<GURL> uninstalled_app_urls_;
@@ -269,6 +277,28 @@
     return {url.value(), code.value()};
   }
 
+  std::pair<GURL, web_app::InstallResultCode> ReinstallPlaceholderAppAndWait(
+      web_app::PendingAppManager* pending_app_manager,
+      web_app::InstallOptions install_options) {
+    base::RunLoop run_loop;
+
+    base::Optional<GURL> url;
+    base::Optional<web_app::InstallResultCode> code;
+
+    pending_app_manager->ReinstallPlaceholderApp(
+        std::move(install_options),
+        base::BindLambdaForTesting(
+            [&](const GURL& app_url,
+                web_app::InstallResultCode install_result_code) {
+              url = app_url;
+              code = install_result_code;
+              run_loop.Quit();
+            }));
+    run_loop.Run();
+
+    return {url.value(), code.value()};
+  }
+
   std::vector<std::pair<GURL, web_app::InstallResultCode>> InstallAppsAndWait(
       web_app::PendingAppManager* pending_app_manager,
       std::vector<web_app::InstallOptions> apps_to_install) {
@@ -1100,6 +1130,7 @@
 
 TEST_F(PendingBookmarkAppManagerTest, UninstallApps_Succeeds) {
   auto pending_app_manager = GetPendingBookmarkAppManagerWithTestFactories();
+  registrar()->AddAsInstalled(GenerateFakeAppId(GURL(kFooWebAppUrl)));
 
   uninstaller()->SetNextResultForTesting(GURL(kFooWebAppUrl), true);
   UninstallAppsResults results = UninstallAppsAndWait(
@@ -1125,6 +1156,8 @@
 
 TEST_F(PendingBookmarkAppManagerTest, UninstallApps_Multiple) {
   auto pending_app_manager = GetPendingBookmarkAppManagerWithTestFactories();
+  registrar()->AddAsInstalled(GenerateFakeAppId(GURL(kFooWebAppUrl)));
+  registrar()->AddAsInstalled(GenerateFakeAppId(GURL(kBarWebAppUrl)));
 
   uninstaller()->SetNextResultForTesting(GURL(kFooWebAppUrl), true);
   uninstaller()->SetNextResultForTesting(GURL(kBarWebAppUrl), true);
@@ -1164,4 +1197,133 @@
   run_loop.Run();
 }
 
+TEST_F(PendingBookmarkAppManagerTest, ReinstallPlaceholderApp_Success) {
+  auto pending_app_manager = GetPendingBookmarkAppManagerWithTestFactories();
+
+  // Install a placeholder app
+  auto install_options = GetFooInstallOptions();
+  install_options.install_placeholder = true;
+
+  {
+    url_loader()->SetNextLoadUrlResult(
+        GURL(kFooWebAppUrl),
+        web_app::WebAppUrlLoader::Result::kRedirectedUrlLoaded);
+    base::Optional<GURL> url;
+    base::Optional<web_app::InstallResultCode> code;
+    std::tie(url, code) =
+        InstallAndWait(pending_app_manager.get(), install_options);
+    ASSERT_EQ(web_app::InstallResultCode::kSuccess, code.value());
+    EXPECT_EQ(0u, install_run_count());
+    EXPECT_EQ(1u, install_placeholder_run_count());
+  }
+
+  // Reinstall placeholder
+  {
+    url_loader()->SetNextLoadUrlResult(
+        GURL(kFooWebAppUrl), web_app::WebAppUrlLoader::Result::kUrlLoaded);
+    uninstaller()->SetNextResultForTesting(GURL(kFooWebAppUrl), true);
+
+    base::Optional<GURL> url;
+    base::Optional<web_app::InstallResultCode> code;
+    std::tie(url, code) = ReinstallPlaceholderAppAndWait(
+        pending_app_manager.get(), install_options);
+
+    EXPECT_EQ(web_app::InstallResultCode::kSuccess, code.value());
+    EXPECT_EQ(GURL(kFooWebAppUrl), url.value());
+
+    EXPECT_EQ(1u, uninstall_call_count());
+    EXPECT_EQ(GURL(kFooWebAppUrl), last_uninstalled_app_url());
+
+    EXPECT_EQ(1u, install_run_count());
+    EXPECT_EQ(1u, install_placeholder_run_count());
+  }
+}
+
+TEST_F(PendingBookmarkAppManagerTest,
+       ReinstallPlaceholderApp_FailsToUninstall) {
+  auto pending_app_manager = GetPendingBookmarkAppManagerWithTestFactories();
+
+  // Install a placeholder app
+  auto install_options = GetFooInstallOptions();
+  install_options.install_placeholder = true;
+
+  {
+    url_loader()->SetNextLoadUrlResult(
+        GURL(kFooWebAppUrl),
+        web_app::WebAppUrlLoader::Result::kRedirectedUrlLoaded);
+    base::Optional<GURL> url;
+    base::Optional<web_app::InstallResultCode> code;
+    std::tie(url, code) =
+        InstallAndWait(pending_app_manager.get(), install_options);
+    ASSERT_EQ(web_app::InstallResultCode::kSuccess, code.value());
+    EXPECT_EQ(0u, install_run_count());
+    EXPECT_EQ(1u, install_placeholder_run_count());
+  }
+
+  // Reinstall placeholder
+  {
+    url_loader()->SetNextLoadUrlResult(
+        GURL(kFooWebAppUrl), web_app::WebAppUrlLoader::Result::kUrlLoaded);
+    uninstaller()->SetNextResultForTesting(GURL(kFooWebAppUrl), false);
+
+    base::Optional<GURL> url;
+    base::Optional<web_app::InstallResultCode> code;
+    std::tie(url, code) = ReinstallPlaceholderAppAndWait(
+        pending_app_manager.get(), install_options);
+
+    EXPECT_EQ(web_app::InstallResultCode::kFailedUnknownReason, code.value());
+    EXPECT_EQ(GURL(kFooWebAppUrl), url.value());
+
+    EXPECT_EQ(1u, uninstall_call_count());
+    EXPECT_EQ(GURL(kFooWebAppUrl), last_uninstalled_app_url());
+
+    EXPECT_EQ(0u, install_run_count());
+    EXPECT_EQ(1u, install_placeholder_run_count());
+  }
+}
+
+TEST_F(PendingBookmarkAppManagerTest,
+       ReinstallPlaceholderApp_ReinstallNotPossible) {
+  auto pending_app_manager = GetPendingBookmarkAppManagerWithTestFactories();
+
+  // Install a placeholder app
+  auto install_options = GetFooInstallOptions();
+  install_options.install_placeholder = true;
+
+  {
+    url_loader()->SetNextLoadUrlResult(
+        GURL(kFooWebAppUrl),
+        web_app::WebAppUrlLoader::Result::kRedirectedUrlLoaded);
+    base::Optional<GURL> url;
+    base::Optional<web_app::InstallResultCode> code;
+    std::tie(url, code) =
+        InstallAndWait(pending_app_manager.get(), install_options);
+    ASSERT_EQ(web_app::InstallResultCode::kSuccess, code.value());
+    EXPECT_EQ(0u, install_run_count());
+    EXPECT_EQ(1u, install_placeholder_run_count());
+  }
+
+  // Reinstall placeholder
+  {
+    url_loader()->SetNextLoadUrlResult(
+        GURL(kFooWebAppUrl),
+        web_app::WebAppUrlLoader::Result::kRedirectedUrlLoaded);
+    uninstaller()->SetNextResultForTesting(GURL(kFooWebAppUrl), true);
+
+    base::Optional<GURL> url;
+    base::Optional<web_app::InstallResultCode> code;
+    std::tie(url, code) = ReinstallPlaceholderAppAndWait(
+        pending_app_manager.get(), install_options);
+
+    EXPECT_EQ(web_app::InstallResultCode::kSuccess, code.value());
+    EXPECT_EQ(GURL(kFooWebAppUrl), url.value());
+
+    EXPECT_EQ(1u, uninstall_call_count());
+    EXPECT_EQ(GURL(kFooWebAppUrl), last_uninstalled_app_url());
+
+    EXPECT_EQ(0u, install_run_count());
+    EXPECT_EQ(2u, install_placeholder_run_count());
+  }
+}
+
 }  // namespace extensions
diff --git a/chrome/browser/web_applications/extensions/web_app_extension_ids_map.cc b/chrome/browser/web_applications/extensions/web_app_extension_ids_map.cc
index 97a42897..b76ab0b 100644
--- a/chrome/browser/web_applications/extensions/web_app_extension_ids_map.cc
+++ b/chrome/browser/web_applications/extensions/web_app_extension_ids_map.cc
@@ -178,6 +178,23 @@
   return base::nullopt;
 }
 
+base::Optional<std::string> ExtensionIdsMap::LookupPlaceholderAppId(
+    const GURL& url) const {
+  DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
+
+  const base::Value* entry =
+      pref_service_->GetDictionary(prefs::kWebAppsExtensionIDs)
+          ->FindKey(url.spec());
+  if (!entry)
+    return base::nullopt;
+
+  base::Optional<bool> is_placeholder = entry->FindBoolKey(kIsPlaceholder);
+  if (!is_placeholder.has_value() || !is_placeholder.value())
+    return base::nullopt;
+
+  return *entry->FindStringKey(kExtensionId);
+}
+
 void ExtensionIdsMap::SetIsPlaceholder(const GURL& url, bool is_placeholder) {
   DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
 
diff --git a/chrome/browser/web_applications/extensions/web_app_extension_ids_map.h b/chrome/browser/web_applications/extensions/web_app_extension_ids_map.h
index f83c847..3f1f7632 100644
--- a/chrome/browser/web_applications/extensions/web_app_extension_ids_map.h
+++ b/chrome/browser/web_applications/extensions/web_app_extension_ids_map.h
@@ -50,6 +50,11 @@
               const std::string& extension_id,
               InstallSource install_source);
   base::Optional<std::string> LookupExtensionId(const GURL& url) const;
+
+  // Returns an id if there is a placeholder app for |url|. Note that nullopt
+  // does not mean that there is no app for |url| just that there is no
+  // *placeholder app*.
+  base::Optional<std::string> LookupPlaceholderAppId(const GURL& url) const;
   void SetIsPlaceholder(const GURL& url, bool is_placeholder);
 
  private:
diff --git a/chrome/browser/webauth_interactive_uitest.cc b/chrome/browser/webauth_interactive_uitest.cc
index 9334b3c..60b77ab 100644
--- a/chrome/browser/webauth_interactive_uitest.cc
+++ b/chrome/browser/webauth_interactive_uitest.cc
@@ -3,14 +3,15 @@
 // found in the LICENSE file.
 
 #include "base/bind.h"
+#include "base/test/bind_test_util.h"
 #include "build/build_config.h"
 #include "chrome/browser/devtools/devtools_window_testing.h"
-#include "chrome/browser/permissions/permission_request_manager.h"
 #include "chrome/browser/ui/browser.h"
 #include "chrome/browser/ui/browser_commands.h"
+#include "chrome/browser/webauthn/authenticator_request_scheduler.h"
+#include "chrome/browser/webauthn/chrome_authenticator_request_delegate.h"
 #include "chrome/test/base/in_process_browser_test.h"
 #include "chrome/test/base/interactive_test_utils.h"
-#include "chrome/test/base/ui_test_utils.h"
 #include "components/network_session_configurator/common/network_switches.h"
 #include "content/public/test/browser_test_utils.h"
 #include "content/public/test/test_service_manager_context.h"
@@ -22,9 +23,11 @@
 namespace {
 
 class WebAuthFocusTest : public InProcessBrowserTest,
-                         public PermissionRequestManager::Observer {
+                         public AuthenticatorRequestDialogModel::Observer {
  protected:
-  WebAuthFocusTest() : https_server_(net::EmbeddedTestServer::TYPE_HTTPS) {}
+  WebAuthFocusTest()
+      : https_server_(net::EmbeddedTestServer::TYPE_HTTPS),
+        permission_requested_(false) {}
 
   void SetUpOnMainThread() override {
     host_resolver()->AddRule("*", "127.0.0.1");
@@ -37,50 +40,33 @@
     return https_server_.GetURL(hostname, relative_url);
   }
 
-  // PermissionRequestManager::Observer implementation
-  void OnBubbleAdded() override {
-    // If this object is registered as a PermissionRequestManager observer then
-    // it'll attempt to complete all permissions bubbles by sending keystrokes.
-    // Note, however, that macOS rejects the permission bubble while other
-    // platforms accept it, because there's no key sequence for accepting a
-    // bubble on macOS.
-    base::ThreadTaskRunnerHandle::Get()->PostTask(
-        FROM_HERE,
-        base::BindOnce(
-            [](Browser* browser) {
-              for (const auto& key : std::vector<ui::KeyboardCode> {
-#if defined(OS_WIN) || defined(OS_CHROMEOS)
-                     // Press tab (to select the "Allow" button of the
-                     // permissions prompt) and then enter to activate it.
-                     ui::KeyboardCode::VKEY_TAB, ui::KeyboardCode::VKEY_RETURN,
-#elif defined(OS_MACOSX)
-                       // There is no way to allow the bubble, we have to
-                       // press escape to reject it.
-                       ui::KeyboardCode::VKEY_ESCAPE,
-#else
-                       // Press tab twice (to select the "Allow" button of the
-                       // permissions prompt) and then enter to activate it.
-                       ui::KeyboardCode::VKEY_TAB,
-                       ui::KeyboardCode::VKEY_TAB,
-                       ui::KeyboardCode::VKEY_RETURN,
-#endif
-                   }) {
-                ASSERT_TRUE(ui_test_utils::SendKeyPressSync(
-                    browser, key,
-                    /*control=*/false, /*shift=*/false, /*alt=*/false,
-                    /*command=*/false));
-              }
-            },
-            browser()));
-  }
+  bool permission_requested() { return permission_requested_; }
+
+  AuthenticatorRequestDialogModel* dialog_model_;
 
  private:
   void SetUpCommandLine(base::CommandLine* command_line) override {
     command_line->AppendSwitch(switches::kIgnoreCertificateErrors);
   }
 
+  // AuthenticatorRequestDialogModel::Observer:
+  void OnStepTransition() override {
+    if (dialog_model_->current_step() !=
+        AuthenticatorRequestDialogModel::Step::kAttestationPermissionRequest)
+      return;
+
+    // Simulate accepting the permission request.
+    dialog_model_->OnAttestationPermissionResponse(true);
+    permission_requested_ = true;
+  }
+
+  void OnModelDestroyed() override {}
+
   net::EmbeddedTestServer https_server_;
 
+  // Set to true when the permission sheet is triggered.
+  bool permission_requested_;
+
   DISALLOW_COPY_AND_ASSIGN(WebAuthFocusTest);
 };
 
@@ -182,26 +168,23 @@
   EXPECT_EQ(result, "OK");
 
   // Requesting "direct" attestation will trigger a permissions prompt.
+  virtual_device.mutable_state()->simulate_press_callback =
+      base::BindLambdaForTesting([&]() {
+        dialog_model_ =
+            AuthenticatorRequestScheduler::GetRequestDelegateForTest(
+                initial_web_contents)
+                ->WeakDialogModelForTesting();
+        dialog_model_->AddObserver(this);
+      });
+
   const std::string get_assertion_with_attestation_script =
       base::ReplaceStringPlaceholders(
           kRegisterTemplate, std::vector<std::string>{"direct"}, nullptr);
-
-  PermissionRequestManager* const permission_request_manager =
-      PermissionRequestManager::FromWebContents(initial_web_contents);
-  // The observer callback will trigger the permissions prompt.
-  permission_request_manager->AddObserver(this);
   ASSERT_TRUE(content::ExecuteScriptAndExtractString(
       initial_web_contents, get_assertion_with_attestation_script, &result));
-#if defined(OS_MACOSX)
-  // The permissions bubble has to be rejected on macOS because there's no key
-  // sequence to accept it. Therefore a NotAllowedError is expected. This is not
-  // ideal as a timeout causes the same result, but it is distinct from a focus
-  // error.
-  EXPECT_THAT(result, ::testing::HasSubstr("NotAllowedError: "));
-#else
+
+  EXPECT_TRUE(permission_requested());
   EXPECT_EQ(result, "OK");
-#endif
-  permission_request_manager->RemoveObserver(this);
 }
 
 }  // anonymous namespace
diff --git a/chrome/browser/webauthn/authenticator_request_dialog_model.cc b/chrome/browser/webauthn/authenticator_request_dialog_model.cc
index e7a063fa..0268dea8 100644
--- a/chrome/browser/webauthn/authenticator_request_dialog_model.cc
+++ b/chrome/browser/webauthn/authenticator_request_dialog_model.cc
@@ -473,6 +473,14 @@
   has_attempted_pin_entry_ = true;
 }
 
+void AuthenticatorRequestDialogModel::OnAttestationPermissionResponse(
+    bool attestation_permission_granted) {
+  if (!attestation_callback_) {
+    return;
+  }
+  std::move(attestation_callback_).Run(attestation_permission_granted);
+}
+
 void AuthenticatorRequestDialogModel::AddAuthenticator(
     const device::FidoAuthenticator& authenticator) {
   if (!authenticator.AuthenticatorTransport()) {
@@ -568,3 +576,9 @@
     SetCurrentStep(Step::kClientPinSetup);
   }
 }
+
+void AuthenticatorRequestDialogModel::RequestAttestationPermission(
+    base::OnceCallback<void(bool)> callback) {
+  attestation_callback_ = std::move(callback);
+  SetCurrentStep(Step::kAttestationPermissionRequest);
+}
diff --git a/chrome/browser/webauthn/authenticator_request_dialog_model.h b/chrome/browser/webauthn/authenticator_request_dialog_model.h
index fc04162..789864b 100644
--- a/chrome/browser/webauthn/authenticator_request_dialog_model.h
+++ b/chrome/browser/webauthn/authenticator_request_dialog_model.h
@@ -97,6 +97,9 @@
 
     // Account selection,
     kSelectAccount,
+
+    // Attestation permission request.
+    kAttestationPermissionRequest,
   };
 
   // Implemented by the dialog to observe this model and show the UI panels
@@ -323,6 +326,10 @@
   // OnHavePIN is called when the user enters a PIN in the UI.
   void OnHavePIN(const std::string& pin);
 
+  // OnAttestationPermissionResponse is called when the user either allows or
+  // disallows an attestation permission request.
+  void OnAttestationPermissionResponse(bool attestation_permission_granted);
+
   void UpdateAuthenticatorReferenceId(base::StringPiece old_authenticator_id,
                                       std::string new_authenticator_id);
   void AddAuthenticator(const device::FidoAuthenticator& authenticator);
@@ -358,6 +365,8 @@
   bool has_attempted_pin_entry() const { return has_attempted_pin_entry_; }
   base::Optional<int> pin_attempts() const { return pin_attempts_; }
 
+  void RequestAttestationPermission(base::OnceCallback<void(bool)> callback);
+
   const std::vector<device::AuthenticatorGetAssertionResponse>& responses() {
     return responses_;
   }
@@ -414,6 +423,8 @@
   bool has_attempted_pin_entry_ = false;
   base::Optional<int> pin_attempts_;
 
+  base::OnceCallback<void(bool)> attestation_callback_;
+
   // responses_ contains possible accounts to select between.
   std::vector<device::AuthenticatorGetAssertionResponse> responses_;
   base::OnceCallback<void(device::AuthenticatorGetAssertionResponse)>
diff --git a/chrome/browser/webauthn/authenticator_request_scheduler.cc b/chrome/browser/webauthn/authenticator_request_scheduler.cc
index 9a28341..fb49996 100644
--- a/chrome/browser/webauthn/authenticator_request_scheduler.cc
+++ b/chrome/browser/webauthn/authenticator_request_scheduler.cc
@@ -58,3 +58,12 @@
   active_request_holder->request() = request->AsWeakPtr();
   return request;
 }
+
+// static
+ChromeAuthenticatorRequestDelegate*
+AuthenticatorRequestScheduler::GetRequestDelegateForTest(
+    content::WebContents* web_contents) {
+  return ActiveRequestWeakHolder::EnsureForWebContents(web_contents)
+      ->request()
+      .get();
+}
diff --git a/chrome/browser/webauthn/authenticator_request_scheduler.h b/chrome/browser/webauthn/authenticator_request_scheduler.h
index 4e0163e..f70ed3e 100644
--- a/chrome/browser/webauthn/authenticator_request_scheduler.h
+++ b/chrome/browser/webauthn/authenticator_request_scheduler.h
@@ -13,6 +13,7 @@
 
 namespace content {
 class RenderFrameHost;
+class WebContents;
 }
 
 // Responsible for scheduling simultaneous Web Authentication API requests
@@ -31,6 +32,11 @@
   static std::unique_ptr<ChromeAuthenticatorRequestDelegate>
   CreateRequestDelegate(content::RenderFrameHost* render_frame_host);
 
+  // Returns the current request delegate associated to the |web_contents| or
+  // nullptr if there is none.
+  static ChromeAuthenticatorRequestDelegate* GetRequestDelegateForTest(
+      content::WebContents* web_contents);
+
  private:
   DISALLOW_COPY_AND_ASSIGN(AuthenticatorRequestScheduler);
 };
diff --git a/chrome/browser/webauthn/chrome_authenticator_request_delegate.cc b/chrome/browser/webauthn/chrome_authenticator_request_delegate.cc
index 16b5d6e..401433f 100644
--- a/chrome/browser/webauthn/chrome_authenticator_request_delegate.cc
+++ b/chrome/browser/webauthn/chrome_authenticator_request_delegate.cc
@@ -14,11 +14,8 @@
 #include "base/feature_list.h"
 #include "base/location.h"
 #include "base/stl_util.h"
-#include "base/threading/thread_task_runner_handle.h"
 #include "base/values.h"
 #include "build/build_config.h"
-#include "chrome/browser/permissions/attestation_permission_request.h"
-#include "chrome/browser/permissions/permission_request_manager.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/ui/browser.h"
 #include "chrome/browser/ui/browser_dialogs.h"
@@ -112,6 +109,11 @@
   return weak_ptr_factory_.GetWeakPtr();
 }
 
+AuthenticatorRequestDialogModel*
+ChromeAuthenticatorRequestDelegate::WeakDialogModelForTesting() const {
+  return weak_dialog_model_;
+}
+
 content::BrowserContext* ChromeAuthenticatorRequestDelegate::browser_context()
     const {
   return content::WebContents::FromRenderFrameHost(render_frame_host())
@@ -204,29 +206,13 @@
     return;
   }
 
-  // This does not use content::PermissionControllerDelegate because that only
-  // works with content settings, while this permission is a non-persisted,
-  // per-attested- registration consent.
-  auto* permission_request_manager = PermissionRequestManager::FromWebContents(
-      content::WebContents::FromRenderFrameHost(render_frame_host()));
-  if (!permission_request_manager) {
-    std::move(callback).Run(false);
-    return;
+  if (!IsWebAuthnUIEnabled()) {
+    // Enable the UI to show the user the permission dialog.
+    ShowAuthenticatorRequestDialog(
+        content::WebContents::FromRenderFrameHost(render_frame_host()),
+        std::move(transient_dialog_model_holder_));
   }
-
-  // The created AttestationPermissionRequest deletes itself once complete.
-  //
-  // |callback| is called via the |MessageLoop| because otherwise the
-  // permissions bubble will have focus and |AuthenticatorImpl| checks that the
-  // frame still has focus before returning any results.
-  permission_request_manager->AddRequest(NewAttestationPermissionRequest(
-      render_frame_host()->GetLastCommittedOrigin(),
-      base::BindOnce(
-          [](base::OnceCallback<void(bool)> callback, bool result) {
-            base::ThreadTaskRunnerHandle::Get()->PostTask(
-                FROM_HERE, base::BindOnce(std::move(callback), result));
-          },
-          std::move(callback))));
+  weak_dialog_model_->RequestAttestationPermission(std::move(callback));
 #endif
 }
 
diff --git a/chrome/browser/webauthn/chrome_authenticator_request_delegate.h b/chrome/browser/webauthn/chrome_authenticator_request_delegate.h
index 68d3e98..95d7b362 100644
--- a/chrome/browser/webauthn/chrome_authenticator_request_delegate.h
+++ b/chrome/browser/webauthn/chrome_authenticator_request_delegate.h
@@ -57,6 +57,8 @@
 
   base::WeakPtr<ChromeAuthenticatorRequestDelegate> AsWeakPtr();
 
+  AuthenticatorRequestDialogModel* WeakDialogModelForTesting() const;
+
  private:
   FRIEND_TEST_ALL_PREFIXES(ChromeAuthenticatorRequestDelegateTest,
                            TestTransportPrefType);
diff --git a/chrome/renderer/extensions/extension_hooks_delegate.cc b/chrome/renderer/extensions/extension_hooks_delegate.cc
index 3f0513a..bbd7271b 100644
--- a/chrome/renderer/extensions/extension_hooks_delegate.cc
+++ b/chrome/renderer/extensions/extension_hooks_delegate.cc
@@ -77,8 +77,13 @@
   if (!has_property.IsJust() || !has_property.FromJust())
     return;
 
-  info.GetReturnValue().Set(
-      runtime_obj->Get(context, property_name).ToLocalChecked());
+  v8::Local<v8::Value> property_value;
+  // Try and grab the chrome.runtime version. It's possible this has been
+  // tampered with, so early-out if an exception is thrown.
+  if (!runtime_obj->Get(context, property_name).ToLocal(&property_value))
+    return;
+
+  info.GetReturnValue().Set(property_value);
 }
 
 // A helper method to throw a deprecation error on access.
diff --git a/chrome/renderer/extensions/extension_hooks_delegate_unittest.cc b/chrome/renderer/extensions/extension_hooks_delegate_unittest.cc
index ad7f5d0..be0703f 100644
--- a/chrome/renderer/extensions/extension_hooks_delegate_unittest.cc
+++ b/chrome/renderer/extensions/extension_hooks_delegate_unittest.cc
@@ -212,4 +212,29 @@
       messaging_service()->HasPortForTesting(script_context(), port_id));
 }
 
+// Tests that overriding the runtime equivalents of chrome.extension methods
+// with accessors that throw does not cause a crash on access. Regression test
+// for https://crbug.com/949170.
+TEST_F(ExtensionHooksDelegateTest, RuntimeAliasesCorrupted) {
+  v8::HandleScope handle_scope(isolate());
+  v8::Local<v8::Context> context = MainContext();
+
+  // Set a trap on chrome.runtime.sendMessage.
+  constexpr char kMutateChromeRuntime[] =
+      R"((function() {
+           Object.defineProperty(
+               chrome.runtime, 'sendMessage',
+               { get() { throw new Error('haha'); } });
+         }))";
+  RunFunctionOnGlobal(FunctionFromString(context, kMutateChromeRuntime),
+                      context, 0, nullptr);
+
+  // Touch chrome.extension.sendMessage, which is aliased to the runtime
+  // version. Though an error is thrown, we shouldn't crash.
+  constexpr char kTouchExtensionSendMessage[] =
+      "(function() { chrome.extension.sendMessage; })";
+  RunFunctionOnGlobal(FunctionFromString(context, kTouchExtensionSendMessage),
+                      context, 0, nullptr);
+}
+
 }  // namespace extensions
diff --git a/chrome/test/BUILD.gn b/chrome/test/BUILD.gn
index aee2876..843e48e 100644
--- a/chrome/test/BUILD.gn
+++ b/chrome/test/BUILD.gn
@@ -629,7 +629,6 @@
       "../browser/browsing_data/counters/passwords_counter_browsertest.cc",
       "../browser/browsing_data/counters/sync_aware_counter_browsertest.cc",
       "../browser/browsing_data/navigation_entry_remover_browsertest.cc",
-      "../browser/chrome_browser_main_browsertest.cc",
       "../browser/chrome_content_browser_client_browsertest.cc",
       "../browser/chrome_content_browser_client_browsertest_chromeos.cc",
       "../browser/chrome_do_not_track_browsertest.cc",
@@ -1884,6 +1883,8 @@
         "../browser/chromeos/login/signin/oauth2_browsertest.cc",
         "../browser/chromeos/login/test/enrollment_helper_mixin.cc",
         "../browser/chromeos/login/test/enrollment_helper_mixin.h",
+        "../browser/chromeos/login/test/enrollment_ui_mixin.cc",
+        "../browser/chromeos/login/test/enrollment_ui_mixin.h",
         "../browser/chromeos/login/test/fake_gaia_mixin.cc",
         "../browser/chromeos/login/test/fake_gaia_mixin.h",
         "../browser/chromeos/login/test/hid_controller_mixin.cc",
@@ -5219,7 +5220,10 @@
         # Use only the _chromeos version on Ash / Chrome OS.
         "../browser/ui/views/test/view_event_test_platform_part_default.cc",
       ]
-      sources += [ "../browser/ui/ash/split_view_interactive_uitest.cc" ]
+      sources += [
+        "../browser/ui/ash/drag_to_overview_interactive_uitest.cc",
+        "../browser/ui/ash/split_view_interactive_uitest.cc",
+      ]
     } else {  # ! is_chromeos
       # Non-ChromeOS notifications tests.
       sources += [
diff --git a/chrome/test/data/extensions/api_test/enterprise_platform_keys.crx b/chrome/test/data/extensions/api_test/enterprise_platform_keys.crx
index 3e4b41fc..ef3c032 100644
--- a/chrome/test/data/extensions/api_test/enterprise_platform_keys.crx
+++ b/chrome/test/data/extensions/api_test/enterprise_platform_keys.crx
Binary files differ
diff --git a/chrome/test/data/extensions/api_test/webstore_private/bundle/bmfoocgfinpmkmlbjhcbofejhkhlbchk.crx b/chrome/test/data/extensions/api_test/webstore_private/bundle/bmfoocgfinpmkmlbjhcbofejhkhlbchk.crx
index 99ce36a4..fccc178 100644
--- a/chrome/test/data/extensions/api_test/webstore_private/bundle/bmfoocgfinpmkmlbjhcbofejhkhlbchk.crx
+++ b/chrome/test/data/extensions/api_test/webstore_private/bundle/bmfoocgfinpmkmlbjhcbofejhkhlbchk.crx
Binary files differ
diff --git a/chrome/test/data/extensions/api_test/webstore_private/bundle/mpneghmdnmaolkljkipbhaienajcflfe.crx b/chrome/test/data/extensions/api_test/webstore_private/bundle/mpneghmdnmaolkljkipbhaienajcflfe.crx
index e20ff4d..2ee96b7e 100644
--- a/chrome/test/data/extensions/api_test/webstore_private/bundle/mpneghmdnmaolkljkipbhaienajcflfe.crx
+++ b/chrome/test/data/extensions/api_test/webstore_private/bundle/mpneghmdnmaolkljkipbhaienajcflfe.crx
Binary files differ
diff --git a/chrome/test/data/extensions/api_test/webstore_private/bundle/pkapffpjmiilhlhbibjhamlmdhfneidj.crx b/chrome/test/data/extensions/api_test/webstore_private/bundle/pkapffpjmiilhlhbibjhamlmdhfneidj.crx
index 9ad13fa..bbde66d 100644
--- a/chrome/test/data/extensions/api_test/webstore_private/bundle/pkapffpjmiilhlhbibjhamlmdhfneidj.crx
+++ b/chrome/test/data/extensions/api_test/webstore_private/bundle/pkapffpjmiilhlhbibjhamlmdhfneidj.crx
Binary files differ
diff --git a/chrome/test/data/extensions/platform_apps/extension_view/skeleton.crx b/chrome/test/data/extensions/platform_apps/extension_view/skeleton.crx
index 99912d1..ee5070c0 100644
--- a/chrome/test/data/extensions/platform_apps/extension_view/skeleton.crx
+++ b/chrome/test/data/extensions/platform_apps/extension_view/skeleton.crx
Binary files differ
diff --git a/chrome/test/data/extensions/platform_apps/extension_view/skeleton_two.crx b/chrome/test/data/extensions/platform_apps/extension_view/skeleton_two.crx
index 507e9eb63e9..73eb8a0 100644
--- a/chrome/test/data/extensions/platform_apps/extension_view/skeleton_two.crx
+++ b/chrome/test/data/extensions/platform_apps/extension_view/skeleton_two.crx
Binary files differ
diff --git a/chrome/test/data/extensions/signin_screen_manual_test_app/app_signed_by_webstore.crx b/chrome/test/data/extensions/signin_screen_manual_test_app/app_signed_by_webstore.crx
index deb82fa..9e9d462e 100644
--- a/chrome/test/data/extensions/signin_screen_manual_test_app/app_signed_by_webstore.crx
+++ b/chrome/test/data/extensions/signin_screen_manual_test_app/app_signed_by_webstore.crx
Binary files differ
diff --git a/chrome/test/data/extensions/trivial_extension/extension.crx b/chrome/test/data/extensions/trivial_extension/extension.crx
index cc8c54d..c899595 100644
--- a/chrome/test/data/extensions/trivial_extension/extension.crx
+++ b/chrome/test/data/extensions/trivial_extension/extension.crx
Binary files differ
diff --git a/chrome/test/data/extensions/trivial_platform_app/app.crx b/chrome/test/data/extensions/trivial_platform_app/app.crx
index 80f24f6..6c45322 100644
--- a/chrome/test/data/extensions/trivial_platform_app/app.crx
+++ b/chrome/test/data/extensions/trivial_platform_app/app.crx
Binary files differ
diff --git a/chrome/test/data/policy/policy_test_cases.json b/chrome/test/data/policy/policy_test_cases.json
index 4ece725..1a8204ad 100644
--- a/chrome/test/data/policy/policy_test_cases.json
+++ b/chrome/test/data/policy/policy_test_cases.json
@@ -329,7 +329,7 @@
 
   "PrintingAllowedPinModes": {
     "os": ["chromeos"],
-    "test_policy": { "PrintingAllowedPinModes": "secure" },
+    "test_policy": { "PrintingAllowedPinModes": "pin" },
     "pref_mappings": [
       { "pref": "printing.allowed_pin_modes" }
     ]
@@ -361,7 +361,7 @@
 
   "PrintingPinDefault": {
     "os": ["chromeos"],
-    "test_policy": { "PrintingPinDefault": "secure" },
+    "test_policy": { "PrintingPinDefault": "pin" },
     "pref_mappings": [
       { "pref": "printing.pin_default" }
     ]
diff --git a/chrome/test/data/webui/print_preview/model_settings_policy_test.js b/chrome/test/data/webui/print_preview/model_settings_policy_test.js
index 8a2cf851..f7c10061 100644
--- a/chrome/test/data/webui/print_preview/model_settings_policy_test.js
+++ b/chrome/test/data/webui/print_preview/model_settings_policy_test.js
@@ -257,8 +257,8 @@
        {
          // Policy has no effect, setting unavailable.
          pinCap: {},
-         pinPolicy: print_preview.PinModeRestriction.SECURE,
-         pinDefault: print_preview.PinModeRestriction.SECURE,
+         pinPolicy: print_preview.PinModeRestriction.PIN,
+         pinDefault: print_preview.PinModeRestriction.PIN,
          expectedValue: false,
          expectedAvailable: false,
          expectedManaged: false,
@@ -268,7 +268,7 @@
          // Policy has no effect, setting is not supported.
          pinCap: {supported: false},
          pinPolicy: print_preview.PinModeRestriction.NONE,
-         pinDefault: print_preview.PinModeRestriction.SECURE,
+         pinDefault: print_preview.PinModeRestriction.PIN,
          expectedValue: false,
          expectedAvailable: false,
          expectedManaged: false,
@@ -277,7 +277,7 @@
        {
          // Policy is UNSECURE, setting is not available.
          pinCap: {supported: true},
-         pinPolicy: print_preview.PinModeRestriction.UNSECURE,
+         pinPolicy: print_preview.PinModeRestriction.NO_PIN,
          expectedValue: false,
          expectedAvailable: false,
          expectedManaged: false,
@@ -287,7 +287,7 @@
          // No restriction policy, setting is modifiable.
          pinCap: {supported: true},
          pinPolicy: print_preview.PinModeRestriction.NONE,
-         pinDefault: print_preview.PinModeRestriction.UNSECURE,
+         pinDefault: print_preview.PinModeRestriction.NO_PIN,
          expectedValue: false,
          expectedAvailable: true,
          expectedManaged: false,
@@ -296,9 +296,9 @@
        {
          // Policy overrides default.
          pinCap: {supported: true},
-         pinPolicy: print_preview.PinModeRestriction.SECURE,
+         pinPolicy: print_preview.PinModeRestriction.PIN,
          // Default mismatches restriction and is ignored.
-         pinDefault: print_preview.PinModeRestriction.UNSECURE,
+         pinDefault: print_preview.PinModeRestriction.NO_PIN,
          expectedValue: true,
          expectedAvailable: true,
          expectedManaged: true,
@@ -307,7 +307,7 @@
        {
          // Default defined by policy but setting is modifiable.
          pinCap: {supported: true},
-         pinDefault: print_preview.PinModeRestriction.SECURE,
+         pinDefault: print_preview.PinModeRestriction.PIN,
          expectedValue: true,
          expectedAvailable: true,
          expectedManaged: false,
diff --git a/chromecast/browser/url_request_context_factory.cc b/chromecast/browser/url_request_context_factory.cc
index bd76b7e..b3b6d3d 100644
--- a/chromecast/browser/url_request_context_factory.cc
+++ b/chromecast/browser/url_request_context_factory.cc
@@ -32,6 +32,7 @@
 #include "net/cert_net/nss_ocsp.h"
 #include "net/cookies/cookie_store.h"
 #include "net/dns/host_resolver.h"
+#include "net/dns/host_resolver_manager.h"
 #include "net/http/http_auth_handler_factory.h"
 #include "net/http/http_network_layer.h"
 #include "net/http/http_server_properties_impl.h"
@@ -233,9 +234,8 @@
   if (system_dependencies_initialized_)
     return;
 
-  // TODO(crbug.com/934402): Use a shared HostResolverManager instead of a
-  // global HostResolver.
-  host_resolver_ = net::HostResolver::CreateStandaloneResolver(nullptr);
+  host_resolver_manager_ = std::make_unique<net::HostResolverManager>(
+      net::HostResolver::Options(), nullptr);
   cert_verifier_ = net::CertVerifier::CreateDefault();
   ssl_config_service_.reset(new net::SSLConfigServiceDefaults);
   transport_security_state_.reset(new net::TransportSecurityState());
@@ -252,6 +252,10 @@
   proxy_resolution_service_ =
       net::ProxyResolutionService::CreateUsingSystemProxyResolver(
           std::move(proxy_config_service_), nullptr);
+
+  system_host_resolver_ =
+      net::HostResolver::CreateResolver(host_resolver_manager_.get());
+
   system_dependencies_initialized_ = true;
 }
 
@@ -300,15 +304,18 @@
 
   main_job_factory_ = std::move(top_job_factory);
 
+  main_host_resolver_ =
+      net::HostResolver::CreateResolver(host_resolver_manager_.get());
+
   main_dependencies_initialized_ = true;
 }
 
-void URLRequestContextFactory::InitializeMediaContextDependencies(
-    net::HttpTransactionFactory* transaction_factory) {
+void URLRequestContextFactory::InitializeMediaContextDependencies() {
   if (media_dependencies_initialized_)
     return;
 
-  media_transaction_factory_.reset(transaction_factory);
+  media_host_resolver_ =
+      net::HostResolver::CreateResolver(host_resolver_manager_.get());
   media_dependencies_initialized_ = true;
 }
 
@@ -338,6 +345,18 @@
             << session_params->disable_idle_sockets_close_on_memory_pressure;
 }
 
+std::unique_ptr<net::HttpNetworkSession>
+URLRequestContextFactory::CreateNetworkSession(
+    const net::URLRequestContext* context) {
+  net::HttpNetworkSession::Params session_params;
+  net::HttpNetworkSession::Context session_context;
+  PopulateNetworkSessionParams(IgnoreCertificateErrors(), &session_params);
+  net::URLRequestContextBuilder::SetHttpNetworkSessionComponents(
+      context, &session_context);
+  return std::make_unique<net::HttpNetworkSession>(session_params,
+                                                   session_context);
+}
+
 net::URLRequestContext* URLRequestContextFactory::CreateSystemRequestContext() {
   DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
   InitializeSystemContextDependencies();
@@ -347,15 +366,12 @@
 
   net::URLRequestContext* system_context = new net::URLRequestContext();
   ConfigureURLRequestContext(system_context, system_job_factory_,
-                             system_cookie_store_, system_network_delegate_);
+                             system_cookie_store_, system_network_delegate_,
+                             system_host_resolver_);
 
-  net::HttpNetworkSession::Params session_params;
-  net::HttpNetworkSession::Context session_context;
-  PopulateNetworkSessionParams(IgnoreCertificateErrors(), &session_params);
-  net::URLRequestContextBuilder::SetHttpNetworkSessionComponents(
-      system_context, &session_context);
-  system_transaction_factory_.reset(new net::HttpNetworkLayer(
-      new net::HttpNetworkSession(session_params, session_context)));
+  system_network_session_ = CreateNetworkSession(system_context);
+  system_transaction_factory_ =
+      std::make_unique<net::HttpNetworkLayer>(system_network_session_.get());
   system_context->set_http_transaction_factory(
       system_transaction_factory_.get());
 
@@ -366,18 +382,20 @@
   DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
   DCHECK(main_getter_.get())
       << "Getting MediaRequestContext before MainRequestContext";
-  net::URLRequestContext* main_context = main_getter_->GetURLRequestContext();
 
-  // Set non caching backend.
-  net::HttpNetworkSession* main_session =
-      main_transaction_factory_->GetSession();
-  InitializeMediaContextDependencies(
-      new net::HttpNetworkLayer(main_session));
+  InitializeMediaContextDependencies();
 
+  // Reuse main context dependencies except HostResolver and
+  // HttpTransactionFactory.
   net::URLRequestContext* media_context = new net::URLRequestContext();
-  media_context->CopyFrom(main_context);
-  media_context->set_http_transaction_factory(
-      media_transaction_factory_.get());
+  ConfigureURLRequestContext(media_context, main_job_factory_,
+                             main_cookie_store_, app_network_delegate_,
+                             media_host_resolver_);
+
+  media_network_session_ = CreateNetworkSession(media_context);
+  media_transaction_factory_ =
+      std::make_unique<net::HttpNetworkLayer>(media_network_session_.get());
+  media_context->set_http_transaction_factory(media_transaction_factory_.get());
 
   return media_context;
 }
@@ -396,15 +414,12 @@
 
   net::URLRequestContext* main_context = new net::URLRequestContext();
   ConfigureURLRequestContext(main_context, main_job_factory_,
-                             main_cookie_store_, app_network_delegate_);
+                             main_cookie_store_, app_network_delegate_,
+                             main_host_resolver_);
 
-  net::HttpNetworkSession::Params session_params;
-  net::HttpNetworkSession::Context session_context;
-  PopulateNetworkSessionParams(IgnoreCertificateErrors(), &session_params);
-  net::URLRequestContextBuilder::SetHttpNetworkSessionComponents(
-      main_context, &session_context);
-  main_transaction_factory_.reset(new net::HttpNetworkLayer(
-      new net::HttpNetworkSession(session_params, session_context)));
+  main_network_session_ = CreateNetworkSession(main_context);
+  main_transaction_factory_ =
+      std::make_unique<net::HttpNetworkLayer>(main_network_session_.get());
   main_context->set_http_transaction_factory(main_transaction_factory_.get());
 
   return main_context;
@@ -414,9 +429,9 @@
     net::URLRequestContext* context,
     const std::unique_ptr<net::URLRequestJobFactory>& job_factory,
     const std::unique_ptr<net::CookieStore>& cookie_store,
-    const std::unique_ptr<CastNetworkDelegate>& network_delegate) {
+    const std::unique_ptr<CastNetworkDelegate>& network_delegate,
+    const std::unique_ptr<net::HostResolver>& host_resolver) {
   // common settings
-  context->set_host_resolver(host_resolver_.get());
   context->set_channel_id_service(channel_id_service_.get());
   context->set_cert_verifier(cert_verifier_.get());
   context->set_cert_transparency_verifier(cert_transparency_verifier_.get());
@@ -433,6 +448,9 @@
   context->set_job_factory(job_factory.get());
   context->set_cookie_store(cookie_store.get());
   context->set_network_delegate(network_delegate.get());
+  context->set_host_resolver(host_resolver.get());
+
+  host_resolver->SetRequestContext(context);
 }
 
 void URLRequestContextFactory::InitializeNetworkDelegates() {
diff --git a/chromecast/browser/url_request_context_factory.h b/chromecast/browser/url_request_context_factory.h
index 9864eb8..3337110 100644
--- a/chromecast/browser/url_request_context_factory.h
+++ b/chromecast/browser/url_request_context_factory.h
@@ -20,6 +20,8 @@
 namespace net {
 class ChannelIDService;
 class CookieStore;
+class HostResolver;
+class HostResolverManager;
 class HttpTransactionFactory;
 class HttpUserAgentSettings;
 class NetLog;
@@ -60,10 +62,6 @@
     return app_network_delegate_.get();
   }
 
-  net::HostResolver* host_resolver() const {
-    return host_resolver_.get();
-  }
-
   // Initialize the CastNetworkDelegate objects. This needs to be done
   // after the CastService is created, but before any URL requests are made.
   void InitializeNetworkDelegates();
@@ -78,11 +76,13 @@
   void InitializeMainContextDependencies(
       content::ProtocolHandlerMap* protocol_handlers,
       content::URLRequestInterceptorScopedVector request_interceptors);
-  void InitializeMediaContextDependencies(net::HttpTransactionFactory* factory);
+  void InitializeMediaContextDependencies();
 
   void PopulateNetworkSessionParams(
       bool ignore_certificate_errors,
       net::HttpNetworkSession::Params* session_params);
+  std::unique_ptr<net::HttpNetworkSession> CreateNetworkSession(
+      const net::URLRequestContext* context);
 
   // These are called by the RequestContextGetters to create each
   // RequestContext.
@@ -99,7 +99,8 @@
       net::URLRequestContext* context,
       const std::unique_ptr<net::URLRequestJobFactory>& job_factory,
       const std::unique_ptr<net::CookieStore>& cookie_store,
-      const std::unique_ptr<CastNetworkDelegate>& network_delegate);
+      const std::unique_ptr<CastNetworkDelegate>& network_delegate,
+      const std::unique_ptr<net::HostResolver>& host_resolver);
 
   scoped_refptr<net::URLRequestContextGetter> system_getter_;
   scoped_refptr<net::URLRequestContextGetter> media_getter_;
@@ -113,7 +114,7 @@
   // The URLRequestContextStorage class manages dependent resources for a single
   // instance of URLRequestContext only.
   bool system_dependencies_initialized_;
-  std::unique_ptr<net::HostResolver> host_resolver_;
+  std::unique_ptr<net::HostResolverManager> host_resolver_manager_;
   std::unique_ptr<net::ChannelIDService> channel_id_service_;
   std::unique_ptr<net::CertVerifier> cert_verifier_;
   std::unique_ptr<net::SSLConfigService> ssl_config_service_;
@@ -125,17 +126,23 @@
   std::unique_ptr<net::HttpAuthHandlerFactory> http_auth_handler_factory_;
   std::unique_ptr<net::HttpServerProperties> http_server_properties_;
   std::unique_ptr<net::HttpUserAgentSettings> http_user_agent_settings_;
+  std::unique_ptr<net::HttpNetworkSession> system_network_session_;
   std::unique_ptr<net::HttpTransactionFactory> system_transaction_factory_;
   std::unique_ptr<net::CookieStore> system_cookie_store_;
   std::unique_ptr<net::URLRequestJobFactory> system_job_factory_;
+  std::unique_ptr<net::HostResolver> system_host_resolver_;
 
   bool main_dependencies_initialized_;
+  std::unique_ptr<net::HttpNetworkSession> main_network_session_;
   std::unique_ptr<net::HttpTransactionFactory> main_transaction_factory_;
   std::unique_ptr<net::CookieStore> main_cookie_store_;
   std::unique_ptr<net::URLRequestJobFactory> main_job_factory_;
+  std::unique_ptr<net::HostResolver> main_host_resolver_;
 
   bool media_dependencies_initialized_;
+  std::unique_ptr<net::HttpNetworkSession> media_network_session_;
   std::unique_ptr<net::HttpTransactionFactory> media_transaction_factory_;
+  std::unique_ptr<net::HostResolver> media_host_resolver_;
 
   std::unique_ptr<PrefProxyConfigTracker> pref_proxy_config_tracker_impl_;
 
diff --git a/chromeos/dbus/BUILD.gn b/chromeos/dbus/BUILD.gn
index 9a62e07..cd8e95b 100644
--- a/chromeos/dbus/BUILD.gn
+++ b/chromeos/dbus/BUILD.gn
@@ -13,6 +13,7 @@
   public_deps = [
     ":common",
     "//chromeos/dbus/constants",
+    "//chromeos/dbus/shill",
     "//dbus",
   ]
   deps = [
@@ -134,48 +135,6 @@
     "runtime_probe_client.h",
     "seneschal_client.cc",
     "seneschal_client.h",
-    "shill/fake_gsm_sms_client.cc",
-    "shill/fake_gsm_sms_client.h",
-    "shill/fake_modem_messaging_client.cc",
-    "shill/fake_modem_messaging_client.h",
-    "shill/fake_shill_device_client.cc",
-    "shill/fake_shill_device_client.h",
-    "shill/fake_shill_ipconfig_client.cc",
-    "shill/fake_shill_ipconfig_client.h",
-    "shill/fake_shill_manager_client.cc",
-    "shill/fake_shill_manager_client.h",
-    "shill/fake_shill_profile_client.cc",
-    "shill/fake_shill_profile_client.h",
-    "shill/fake_shill_service_client.cc",
-    "shill/fake_shill_service_client.h",
-    "shill/fake_shill_third_party_vpn_driver_client.cc",
-    "shill/fake_shill_third_party_vpn_driver_client.h",
-    "shill/fake_sms_client.cc",
-    "shill/fake_sms_client.h",
-    "shill/gsm_sms_client.cc",
-    "shill/gsm_sms_client.h",
-    "shill/modem_messaging_client.cc",
-    "shill/modem_messaging_client.h",
-    "shill/shill_client_helper.cc",
-    "shill/shill_client_helper.h",
-    "shill/shill_clients.cc",
-    "shill/shill_clients.h",
-    "shill/shill_device_client.cc",
-    "shill/shill_device_client.h",
-    "shill/shill_ipconfig_client.cc",
-    "shill/shill_ipconfig_client.h",
-    "shill/shill_manager_client.cc",
-    "shill/shill_manager_client.h",
-    "shill/shill_profile_client.cc",
-    "shill/shill_profile_client.h",
-    "shill/shill_property_changed_observer.h",
-    "shill/shill_service_client.cc",
-    "shill/shill_service_client.h",
-    "shill/shill_third_party_vpn_driver_client.cc",
-    "shill/shill_third_party_vpn_driver_client.h",
-    "shill/shill_third_party_vpn_observer.h",
-    "shill/sms_client.cc",
-    "shill/sms_client.h",
     "smb_provider_client.cc",
     "smb_provider_client.h",
     "update_engine_client.cc",
@@ -244,6 +203,7 @@
     "//chromeos/dbus/cryptohome",
     "//chromeos/dbus/cryptohome:attestation_proto",
     "//chromeos/dbus/session_manager",
+    "//chromeos/dbus/shill:test_support",
     "//components/account_id",
     "//dbus",
     "//dbus:test_support",
@@ -268,16 +228,6 @@
     "power/fake_power_manager_client_unittest.cc",
     "power/power_manager_client_unittest.cc",
     "power/power_policy_controller_unittest.cc",
-    "shill/gsm_sms_client_unittest.cc",
-    "shill/modem_messaging_client_unittest.cc",
-    "shill/shill_client_unittest_base.cc",
-    "shill/shill_client_unittest_base.h",
-    "shill/shill_device_client_unittest.cc",
-    "shill/shill_ipconfig_client_unittest.cc",
-    "shill/shill_manager_client_unittest.cc",
-    "shill/shill_profile_client_unittest.cc",
-    "shill/shill_service_client_unittest.cc",
-    "shill/shill_third_party_vpn_driver_client_unittest.cc",
     "update_engine_client_unittest.cc",
     "util/version_loader_unittest.cc",
   ]
diff --git a/chromeos/dbus/shill/BUILD.gn b/chromeos/dbus/shill/BUILD.gn
new file mode 100644
index 0000000..021cc42
--- /dev/null
+++ b/chromeos/dbus/shill/BUILD.gn
@@ -0,0 +1,91 @@
+# 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.
+
+assert(is_chromeos, "Non-ChromeOS builds cannot depend on //chromeos")
+
+component("shill") {
+  defines = [ "IS_SHILL_CLIENT_IMPL" ]
+
+  deps = [
+    "//base",
+    "//chromeos/dbus:common",
+    "//chromeos/dbus/constants",
+    "//components/device_event_log",
+    "//dbus",
+    "//net",
+  ]
+
+  sources = [
+    "fake_gsm_sms_client.cc",
+    "fake_gsm_sms_client.h",
+    "fake_modem_messaging_client.cc",
+    "fake_modem_messaging_client.h",
+    "fake_shill_device_client.cc",
+    "fake_shill_device_client.h",
+    "fake_shill_ipconfig_client.cc",
+    "fake_shill_ipconfig_client.h",
+    "fake_shill_manager_client.cc",
+    "fake_shill_manager_client.h",
+    "fake_shill_profile_client.cc",
+    "fake_shill_profile_client.h",
+    "fake_shill_service_client.cc",
+    "fake_shill_service_client.h",
+    "fake_shill_third_party_vpn_driver_client.cc",
+    "fake_shill_third_party_vpn_driver_client.h",
+    "fake_sms_client.cc",
+    "fake_sms_client.h",
+    "gsm_sms_client.cc",
+    "gsm_sms_client.h",
+    "modem_messaging_client.cc",
+    "modem_messaging_client.h",
+    "shill_client_helper.cc",
+    "shill_client_helper.h",
+    "shill_clients.cc",
+    "shill_clients.h",
+    "shill_device_client.cc",
+    "shill_device_client.h",
+    "shill_ipconfig_client.cc",
+    "shill_ipconfig_client.h",
+    "shill_manager_client.cc",
+    "shill_manager_client.h",
+    "shill_profile_client.cc",
+    "shill_profile_client.h",
+    "shill_property_changed_observer.h",
+    "shill_service_client.cc",
+    "shill_service_client.h",
+    "shill_third_party_vpn_driver_client.cc",
+    "shill_third_party_vpn_driver_client.h",
+    "shill_third_party_vpn_observer.h",
+    "sms_client.cc",
+    "sms_client.h",
+  ]
+}
+
+source_set("test_support") {
+  testonly = true
+  public_deps = [
+    ":shill",
+  ]
+  deps = [
+    "//base",
+    "//base/test:test_support",
+    "//chromeos/dbus:common",
+    "//dbus:test_support",
+    "//testing/gmock",
+    "//testing/gtest",
+  ]
+
+  sources = [
+    "gsm_sms_client_unittest.cc",
+    "modem_messaging_client_unittest.cc",
+    "shill_client_unittest_base.cc",
+    "shill_client_unittest_base.h",
+    "shill_device_client_unittest.cc",
+    "shill_ipconfig_client_unittest.cc",
+    "shill_manager_client_unittest.cc",
+    "shill_profile_client_unittest.cc",
+    "shill_service_client_unittest.cc",
+    "shill_third_party_vpn_driver_client_unittest.cc",
+  ]
+}
diff --git a/chromeos/dbus/shill/fake_gsm_sms_client.h b/chromeos/dbus/shill/fake_gsm_sms_client.h
index 7d86470..12873c1 100644
--- a/chromeos/dbus/shill/fake_gsm_sms_client.h
+++ b/chromeos/dbus/shill/fake_gsm_sms_client.h
@@ -19,7 +19,7 @@
 namespace chromeos {
 
 // A fake implementation of GsmSMSClient used for tests.
-class COMPONENT_EXPORT(CHROMEOS_DBUS) FakeGsmSMSClient : public GsmSMSClient {
+class COMPONENT_EXPORT(SHILL_CLIENT) FakeGsmSMSClient : public GsmSMSClient {
  public:
   FakeGsmSMSClient();
   ~FakeGsmSMSClient() override;
diff --git a/chromeos/dbus/shill/fake_modem_messaging_client.h b/chromeos/dbus/shill/fake_modem_messaging_client.h
index 9cb26299..316763c0 100644
--- a/chromeos/dbus/shill/fake_modem_messaging_client.h
+++ b/chromeos/dbus/shill/fake_modem_messaging_client.h
@@ -15,7 +15,7 @@
 
 namespace chromeos {
 
-class COMPONENT_EXPORT(CHROMEOS_DBUS) FakeModemMessagingClient
+class COMPONENT_EXPORT(SHILL_CLIENT) FakeModemMessagingClient
     : public ModemMessagingClient {
  public:
   FakeModemMessagingClient();
diff --git a/chromeos/dbus/shill/fake_shill_device_client.h b/chromeos/dbus/shill/fake_shill_device_client.h
index f2ee1670..b598755b 100644
--- a/chromeos/dbus/shill/fake_shill_device_client.h
+++ b/chromeos/dbus/shill/fake_shill_device_client.h
@@ -19,7 +19,7 @@
 
 // A fake implementation of ShillDeviceClient.
 // Implemented: Stub cellular device for SMS testing.
-class COMPONENT_EXPORT(CHROMEOS_DBUS) FakeShillDeviceClient
+class COMPONENT_EXPORT(SHILL_CLIENT) FakeShillDeviceClient
     : public ShillDeviceClient,
       public ShillDeviceClient::TestInterface {
  public:
diff --git a/chromeos/dbus/shill/fake_shill_ipconfig_client.h b/chromeos/dbus/shill/fake_shill_ipconfig_client.h
index 7d332bb..0640142 100644
--- a/chromeos/dbus/shill/fake_shill_ipconfig_client.h
+++ b/chromeos/dbus/shill/fake_shill_ipconfig_client.h
@@ -14,7 +14,7 @@
 namespace chromeos {
 
 // A fake implementation of ShillIPConfigClient.
-class COMPONENT_EXPORT(CHROMEOS_DBUS) FakeShillIPConfigClient
+class COMPONENT_EXPORT(SHILL_CLIENT) FakeShillIPConfigClient
     : public ShillIPConfigClient,
       public ShillIPConfigClient::TestInterface {
  public:
diff --git a/chromeos/dbus/shill/fake_shill_manager_client.h b/chromeos/dbus/shill/fake_shill_manager_client.h
index 703b1da..a5df436 100644
--- a/chromeos/dbus/shill/fake_shill_manager_client.h
+++ b/chromeos/dbus/shill/fake_shill_manager_client.h
@@ -20,7 +20,7 @@
 // A fake implementation of ShillManagerClient. This works in close coordination
 // with FakeShillServiceClient. FakeShillDeviceClient, and
 // FakeShillProfileClient, and is not intended to be used independently.
-class COMPONENT_EXPORT(CHROMEOS_DBUS) FakeShillManagerClient
+class COMPONENT_EXPORT(SHILL_CLIENT) FakeShillManagerClient
     : public ShillManagerClient,
       public ShillManagerClient::TestInterface {
  public:
diff --git a/chromeos/dbus/shill/fake_shill_profile_client.h b/chromeos/dbus/shill/fake_shill_profile_client.h
index 71400f7..8567f7d 100644
--- a/chromeos/dbus/shill/fake_shill_profile_client.h
+++ b/chromeos/dbus/shill/fake_shill_profile_client.h
@@ -17,7 +17,7 @@
 namespace chromeos {
 
 // A stub implementation of ShillProfileClient.
-class COMPONENT_EXPORT(CHROMEOS_DBUS) FakeShillProfileClient
+class COMPONENT_EXPORT(SHILL_CLIENT) FakeShillProfileClient
     : public ShillProfileClient,
       public ShillProfileClient::TestInterface {
  public:
diff --git a/chromeos/dbus/shill/fake_shill_service_client.cc b/chromeos/dbus/shill/fake_shill_service_client.cc
index 4fc8536..e24cfc51 100644
--- a/chromeos/dbus/shill/fake_shill_service_client.cc
+++ b/chromeos/dbus/shill/fake_shill_service_client.cc
@@ -16,7 +16,6 @@
 #include "base/strings/string_util.h"
 #include "base/threading/thread_task_runner_handle.h"
 #include "base/values.h"
-#include "chromeos/dbus/dbus_thread_manager.h"
 #include "chromeos/dbus/shill/shill_device_client.h"
 #include "chromeos/dbus/shill/shill_manager_client.h"
 #include "chromeos/dbus/shill/shill_profile_client.h"
diff --git a/chromeos/dbus/shill/fake_shill_service_client.h b/chromeos/dbus/shill/fake_shill_service_client.h
index 084d487..50549c6 100644
--- a/chromeos/dbus/shill/fake_shill_service_client.h
+++ b/chromeos/dbus/shill/fake_shill_service_client.h
@@ -20,7 +20,7 @@
 
 // A fake implementation of ShillServiceClient. This works in close coordination
 // with FakeShillManagerClient and is not intended to be used independently.
-class COMPONENT_EXPORT(CHROMEOS_DBUS) FakeShillServiceClient
+class COMPONENT_EXPORT(SHILL_CLIENT) FakeShillServiceClient
     : public ShillServiceClient,
       public ShillServiceClient::TestInterface {
  public:
diff --git a/chromeos/dbus/shill/fake_shill_third_party_vpn_driver_client.h b/chromeos/dbus/shill/fake_shill_third_party_vpn_driver_client.h
index a70730f..0a8d041 100644
--- a/chromeos/dbus/shill/fake_shill_third_party_vpn_driver_client.h
+++ b/chromeos/dbus/shill/fake_shill_third_party_vpn_driver_client.h
@@ -21,7 +21,7 @@
 // The client can generate fake DBus signals when
 // ShillThirdPartyVpnDriverClient::TestInterface methods are called. The
 // DBus methods are nops that only acknowledge the caller.
-class COMPONENT_EXPORT(CHROMEOS_DBUS) FakeShillThirdPartyVpnDriverClient
+class COMPONENT_EXPORT(SHILL_CLIENT) FakeShillThirdPartyVpnDriverClient
     : public ShillThirdPartyVpnDriverClient,
       public ShillThirdPartyVpnDriverClient::TestInterface {
  public:
diff --git a/chromeos/dbus/shill/gsm_sms_client.h b/chromeos/dbus/shill/gsm_sms_client.h
index 05aee39..5d65a2a 100644
--- a/chromeos/dbus/shill/gsm_sms_client.h
+++ b/chromeos/dbus/shill/gsm_sms_client.h
@@ -30,7 +30,7 @@
 // org.freedesktop.ModemManager.Modem.Gsm.SMS service.
 // All methods should be called from the origin thread (UI thread) which
 // initializes the DBusThreadManager instance.
-class COMPONENT_EXPORT(CHROMEOS_DBUS) GsmSMSClient {
+class COMPONENT_EXPORT(SHILL_CLIENT) GsmSMSClient {
  public:
   typedef base::Callback<void(uint32_t index, bool complete)>
       SmsReceivedHandler;
diff --git a/chromeos/dbus/shill/modem_messaging_client.h b/chromeos/dbus/shill/modem_messaging_client.h
index b089be41..4f1073a 100644
--- a/chromeos/dbus/shill/modem_messaging_client.h
+++ b/chromeos/dbus/shill/modem_messaging_client.h
@@ -11,7 +11,6 @@
 #include "base/callback.h"
 #include "base/component_export.h"
 #include "base/macros.h"
-#include "chromeos/dbus/dbus_client.h"
 #include "chromeos/dbus/dbus_method_call_status.h"
 
 namespace dbus {
@@ -25,7 +24,7 @@
 // org.freedesktop.ModemManager1.Modem.Messaging service.  All methods
 // should be called from the origin thread (UI thread) which
 // initializes the DBusThreadManager instance.
-class COMPONENT_EXPORT(CHROMEOS_DBUS) ModemMessagingClient {
+class COMPONENT_EXPORT(SHILL_CLIENT) ModemMessagingClient {
  public:
   typedef base::Callback<void(const dbus::ObjectPath& message_path,
                               bool complete)>
diff --git a/chromeos/dbus/shill/shill_clients.h b/chromeos/dbus/shill/shill_clients.h
index aca7564..edba68f 100644
--- a/chromeos/dbus/shill/shill_clients.h
+++ b/chromeos/dbus/shill/shill_clients.h
@@ -15,13 +15,13 @@
 namespace shill_clients {
 
 // Initialize Shill and modemmanager related dbus clients.
-COMPONENT_EXPORT(CHROMEOS_DBUS) void Initialize(dbus::Bus* system_bus);
+COMPONENT_EXPORT(SHILL_CLIENT) void Initialize(dbus::Bus* system_bus);
 
 // Initialize fake Shill and modemmanager related dbus clients.
-COMPONENT_EXPORT(CHROMEOS_DBUS) void InitializeFakes();
+COMPONENT_EXPORT(SHILL_CLIENT) void InitializeFakes();
 
 // Shut down Shill and modemmanager related dbus clients.
-COMPONENT_EXPORT(CHROMEOS_DBUS) void Shutdown();
+COMPONENT_EXPORT(SHILL_CLIENT) void Shutdown();
 
 }  // namespace shill_clients
 }  // namespace chromeos
diff --git a/chromeos/dbus/shill/shill_device_client.h b/chromeos/dbus/shill/shill_device_client.h
index c4f1b03d..d3e97eb5 100644
--- a/chromeos/dbus/shill/shill_device_client.h
+++ b/chromeos/dbus/shill/shill_device_client.h
@@ -33,7 +33,7 @@
 // ShillDeviceClient is used to communicate with the Shill Device service.
 // All methods should be called from the origin thread which initializes the
 // DBusThreadManager instance.
-class COMPONENT_EXPORT(CHROMEOS_DBUS) ShillDeviceClient {
+class COMPONENT_EXPORT(SHILL_CLIENT) ShillDeviceClient {
  public:
   typedef ShillClientHelper::PropertyChangedHandler PropertyChangedHandler;
   typedef ShillClientHelper::DictionaryValueCallback DictionaryValueCallback;
diff --git a/chromeos/dbus/shill/shill_ipconfig_client.h b/chromeos/dbus/shill/shill_ipconfig_client.h
index 272545d..2dd805b 100644
--- a/chromeos/dbus/shill/shill_ipconfig_client.h
+++ b/chromeos/dbus/shill/shill_ipconfig_client.h
@@ -29,7 +29,7 @@
 // ShillIPConfigClient is used to communicate with the Shill IPConfig
 // service.  All methods should be called from the origin thread which
 // initializes the DBusThreadManager instance.
-class COMPONENT_EXPORT(CHROMEOS_DBUS) ShillIPConfigClient {
+class COMPONENT_EXPORT(SHILL_CLIENT) ShillIPConfigClient {
  public:
   typedef ShillClientHelper::PropertyChangedHandler PropertyChangedHandler;
   typedef ShillClientHelper::DictionaryValueCallback DictionaryValueCallback;
diff --git a/chromeos/dbus/shill/shill_manager_client.h b/chromeos/dbus/shill/shill_manager_client.h
index 02900c0..92042eb 100644
--- a/chromeos/dbus/shill/shill_manager_client.h
+++ b/chromeos/dbus/shill/shill_manager_client.h
@@ -24,7 +24,7 @@
 // ShillManagerClient is used to communicate with the Shill Manager
 // service.  All methods should be called from the origin thread which
 // initializes the DBusThreadManager instance.
-class COMPONENT_EXPORT(CHROMEOS_DBUS) ShillManagerClient {
+class COMPONENT_EXPORT(SHILL_CLIENT) ShillManagerClient {
  public:
   typedef ShillClientHelper::PropertyChangedHandler PropertyChangedHandler;
   typedef ShillClientHelper::DictionaryValueCallback DictionaryValueCallback;
diff --git a/chromeos/dbus/shill/shill_profile_client.cc b/chromeos/dbus/shill/shill_profile_client.cc
index 170c422..0782411 100644
--- a/chromeos/dbus/shill/shill_profile_client.cc
+++ b/chromeos/dbus/shill/shill_profile_client.cc
@@ -11,7 +11,6 @@
 #include "base/bind.h"
 #include "base/macros.h"
 #include "base/values.h"
-#include "chromeos/dbus/dbus_thread_manager.h"
 #include "chromeos/dbus/shill/fake_shill_profile_client.h"
 #include "chromeos/dbus/shill/shill_property_changed_observer.h"
 #include "dbus/bus.h"
diff --git a/chromeos/dbus/shill/shill_profile_client.h b/chromeos/dbus/shill/shill_profile_client.h
index b75a452..8c1ca92 100644
--- a/chromeos/dbus/shill/shill_profile_client.h
+++ b/chromeos/dbus/shill/shill_profile_client.h
@@ -29,7 +29,7 @@
 // ShillProfileClient is used to communicate with the Shill Profile
 // service.  All methods should be called from the origin thread which
 // initializes the DBusThreadManager instance.
-class COMPONENT_EXPORT(CHROMEOS_DBUS) ShillProfileClient {
+class COMPONENT_EXPORT(SHILL_CLIENT) ShillProfileClient {
  public:
   typedef ShillClientHelper::PropertyChangedHandler PropertyChangedHandler;
   typedef ShillClientHelper::DictionaryValueCallbackWithoutStatus
diff --git a/chromeos/dbus/shill/shill_service_client.h b/chromeos/dbus/shill/shill_service_client.h
index c3a4658..84f271ec 100644
--- a/chromeos/dbus/shill/shill_service_client.h
+++ b/chromeos/dbus/shill/shill_service_client.h
@@ -29,7 +29,7 @@
 // service.
 // All methods should be called from the origin thread which initializes the
 // DBusThreadManager instance.
-class COMPONENT_EXPORT(CHROMEOS_DBUS) ShillServiceClient {
+class COMPONENT_EXPORT(SHILL_CLIENT) ShillServiceClient {
  public:
   typedef ShillClientHelper::PropertyChangedHandler PropertyChangedHandler;
   typedef ShillClientHelper::DictionaryValueCallback DictionaryValueCallback;
diff --git a/chromeos/dbus/shill/shill_third_party_vpn_driver_client.h b/chromeos/dbus/shill/shill_third_party_vpn_driver_client.h
index 89814686..4ec6abde 100644
--- a/chromeos/dbus/shill/shill_third_party_vpn_driver_client.h
+++ b/chromeos/dbus/shill/shill_third_party_vpn_driver_client.h
@@ -30,7 +30,7 @@
 // ThirdPartyVpnDriver service.
 // All methods should be called from the origin thread which initializes the
 // DBusThreadManager instance.
-class COMPONENT_EXPORT(CHROMEOS_DBUS) ShillThirdPartyVpnDriverClient {
+class COMPONENT_EXPORT(SHILL_CLIENT) ShillThirdPartyVpnDriverClient {
  public:
   class TestInterface {
    public:
diff --git a/chromeos/dbus/shill/sms_client.h b/chromeos/dbus/shill/sms_client.h
index de50158..89a6673e 100644
--- a/chromeos/dbus/shill/sms_client.h
+++ b/chromeos/dbus/shill/sms_client.h
@@ -10,7 +10,6 @@
 #include "base/callback.h"
 #include "base/component_export.h"
 #include "base/macros.h"
-#include "chromeos/dbus/dbus_client.h"
 
 namespace base {
 class Bus;
@@ -18,6 +17,7 @@
 }  // namespace base
 
 namespace dbus {
+class Bus;
 class ObjectPath;
 }
 
@@ -27,7 +27,7 @@
 // org.freedesktop.ModemManager1.SMS service.  All methods should be
 // called from the origin thread (UI thread) which initializes the
 // DBusThreadManager instance.
-class COMPONENT_EXPORT(CHROMEOS_DBUS) SMSClient {
+class COMPONENT_EXPORT(SHILL_CLIENT) SMSClient {
  public:
   using GetAllCallback =
       base::OnceCallback<void(const base::DictionaryValue& sms)>;
diff --git a/chromeos/geolocation/BUILD.gn b/chromeos/geolocation/BUILD.gn
index 525c819..4bc2e9df 100644
--- a/chromeos/geolocation/BUILD.gn
+++ b/chromeos/geolocation/BUILD.gn
@@ -32,7 +32,7 @@
   deps = [
     ":geolocation",
     "//base",
-    "//chromeos/dbus",
+    "//chromeos/dbus/shill",
     "//chromeos/network:test_support",
     "//net",
     "//services/network:test_support",
diff --git a/chromeos/network/BUILD.gn b/chromeos/network/BUILD.gn
index 5dda140..99fb26a 100644
--- a/chromeos/network/BUILD.gn
+++ b/chromeos/network/BUILD.gn
@@ -14,8 +14,9 @@
     "//base",
     "//base:i18n",
     "//chromeos/constants",
-    "//chromeos/dbus",
+    "//chromeos/dbus:common",
     "//chromeos/dbus/permission_broker",
+    "//chromeos/dbus/shill",
     "//chromeos/login/login_state",
     "//components/account_id",
     "//components/certificate_matching",
diff --git a/components/autofill/core/browser/autofill_profile.cc b/components/autofill/core/browser/autofill_profile.cc
index 3acbc8c..6a15e0b 100644
--- a/components/autofill/core/browser/autofill_profile.cc
+++ b/components/autofill/core/browser/autofill_profile.cc
@@ -444,12 +444,6 @@
   return 0;
 }
 
-bool AutofillProfile::EqualsSansOrigin(const AutofillProfile& profile) const {
-  return guid() == profile.guid() &&
-         language_code() == profile.language_code() &&
-         Compare(profile) == 0;
-}
-
 bool AutofillProfile::EqualsForSyncPurposes(const AutofillProfile& profile)
     const {
   return use_count() == profile.use_count() &&
@@ -665,16 +659,6 @@
 }
 
 // static
-bool AutofillProfile::SupportsMultiValue(ServerFieldType type) {
-  FieldTypeGroup group = AutofillType(type).group();
-  return group == NAME ||
-         group == NAME_BILLING ||
-         group == EMAIL ||
-         group == PHONE_HOME ||
-         group == PHONE_BILLING;
-}
-
-// static
 void AutofillProfile::CreateDifferentiatingLabels(
     const std::vector<AutofillProfile*>& profiles,
     const std::string& app_locale,
diff --git a/components/autofill/core/browser/autofill_profile.h b/components/autofill/core/browser/autofill_profile.h
index 716f726e..59b8bf12 100644
--- a/components/autofill/core/browser/autofill_profile.h
+++ b/components/autofill/core/browser/autofill_profile.h
@@ -100,9 +100,6 @@
   // themselves.
   int Compare(const AutofillProfile& profile) const;
 
-  // Same as operator==, but ignores differences in origin.
-  bool EqualsSansOrigin(const AutofillProfile& profile) const;
-
   // Same as operator==, but ignores differences in guid and cares about
   // differences in usage stats.
   bool EqualsForSyncPurposes(const AutofillProfile& profile) const;
@@ -149,9 +146,6 @@
   bool SaveAdditionalInfo(const AutofillProfile& profile,
                           const std::string& app_locale);
 
-  // Returns |true| if |type| accepts multi-values.
-  static bool SupportsMultiValue(ServerFieldType type);
-
   // Creates a differentiating label for each of the |profiles|.
   // Labels consist of the minimal differentiating combination of:
   // 1. Full name.
diff --git a/components/autofill/core/browser/autofill_test_utils.cc b/components/autofill/core/browser/autofill_test_utils.cc
index ffab385a..816aa8d 100644
--- a/components/autofill/core/browser/autofill_test_utils.cc
+++ b/components/autofill/core/browser/autofill_test_utils.cc
@@ -337,12 +337,6 @@
   return profile;
 }
 
-AutofillProfile GetVerifiedProfile2() {
-  AutofillProfile profile(GetFullProfile2());
-  profile.set_origin(kSettingsOrigin);
-  return profile;
-}
-
 AutofillProfile GetServerProfile() {
   AutofillProfile profile(AutofillProfile::SERVER_PROFILE, "id1");
   // Note: server profiles don't have email addresses and only have full names.
diff --git a/components/autofill/core/browser/autofill_test_utils.h b/components/autofill/core/browser/autofill_test_utils.h
index 02eb584..9b74aab 100644
--- a/components/autofill/core/browser/autofill_test_utils.h
+++ b/components/autofill/core/browser/autofill_test_utils.h
@@ -112,9 +112,6 @@
 // Returns a verified profile full of dummy info.
 AutofillProfile GetVerifiedProfile();
 
-// Returns a verified profile full of dummy info, different to the above.
-AutofillProfile GetVerifiedProfile2();
-
 // Returns a server profile full of dummy info.
 AutofillProfile GetServerProfile();
 
@@ -127,12 +124,6 @@
 // Returns a credit card full of dummy info, different to the above.
 CreditCard GetCreditCard2();
 
-// Returns a verified credit card full of dummy info.
-CreditCard GetVerifiedCreditCard();
-
-// Returns a verified credit card full of dummy info, different to the above.
-CreditCard GetVerifiedCreditCard2();
-
 // Returns a masked server card full of dummy info.
 CreditCard GetMaskedServerCard();
 CreditCard GetMaskedServerCardAmex();
diff --git a/components/autofill/core/browser/form_structure.cc b/components/autofill/core/browser/form_structure.cc
index cdfc2f3..08281b2 100644
--- a/components/autofill/core/browser/form_structure.cc
+++ b/components/autofill/core/browser/form_structure.cc
@@ -1230,24 +1230,6 @@
   return values;
 }
 
-base::string16 FormStructure::GetUniqueValue(HtmlFieldType type) const {
-  base::string16 value;
-  for (const auto& field : fields_) {
-    if (field->html_type() != type)
-      continue;
-
-    // More than one value found; abort rather than choosing one arbitrarily.
-    if (!value.empty() && !field->value.empty()) {
-      value.clear();
-      break;
-    }
-
-    value = field->value;
-  }
-
-  return value;
-}
-
 const AutofillField* FormStructure::field(size_t index) const {
   if (index >= fields_.size()) {
     NOTREACHED();
@@ -1728,7 +1710,14 @@
 void FormStructure::RationalizeFieldTypePredictions() {
   RationalizeCreditCardFieldPredictions();
   for (const auto& field : fields_) {
-    field->SetTypeTo(field->Type());
+    if (base::FeatureList::IsEnabled(features::kAutofillOffNoServerData) &&
+        !field->should_autocomplete && field->server_type() == NO_SERVER_DATA) {
+      // When the field has autocomplete off, and the server returned no
+      // prediction, then assume Autofill is not useful for the current field.
+      field->SetTypeTo(AutofillType(UNKNOWN_TYPE));
+    } else {
+      field->SetTypeTo(field->Type());
+    }
   }
 }
 
diff --git a/components/autofill/core/browser/form_structure.h b/components/autofill/core/browser/form_structure.h
index 8f7fdaa..102920d9 100644
--- a/components/autofill/core/browser/form_structure.h
+++ b/components/autofill/core/browser/form_structure.h
@@ -194,10 +194,6 @@
   // All returned values are standardized to upper case.
   std::set<base::string16> PossibleValues(ServerFieldType type);
 
-  // Gets the form's current value for |type|. For example, it may return
-  // the contents of a text input or the currently selected <option>.
-  base::string16 GetUniqueValue(HtmlFieldType type) const;
-
   // Rationalize phone number fields in a given section, that is only fill
   // the fields that are considered composing a first complete phone number.
   void RationalizePhoneNumbersInSection(std::string section);
diff --git a/components/autofill/core/browser/form_structure_unittest.cc b/components/autofill/core/browser/form_structure_unittest.cc
index cdc3cd5..b20f3c9 100644
--- a/components/autofill/core/browser/form_structure_unittest.cc
+++ b/components/autofill/core/browser/form_structure_unittest.cc
@@ -143,6 +143,10 @@
   scoped_refptr<base::FieldTrial> field_trial_;
 };
 
+class ParameterizedFormStructureTest
+    : public FormStructureTest,
+      public testing::WithParamInterface<bool> {};
+
 TEST_F(FormStructureTest, FieldCount) {
   std::unique_ptr<FormStructure> form_structure;
   FormData form;
@@ -6584,6 +6588,81 @@
   EXPECT_EQ(ADDRESS_HOME_STATE, forms[0]->field(5)->Type().GetStorableType());
 }
 
+INSTANTIATE_TEST_SUITE_P(,
+                         ParameterizedFormStructureTest,
+                         testing::Values(true, false));
+
+// Tests that, when the flag is off, we will not set the predicted type to
+// unknown for fields that have no server data and autocomplete off, and when
+// the flag is ON, we will overwrite the predicted type.
+TEST_P(ParameterizedFormStructureTest,
+       NoServerData_AutocompleteOff_FlagDisabled_NoOverwrite) {
+  base::test::ScopedFeatureList scoped_features;
+
+  bool flag_enabled = GetParam();
+  scoped_features.InitWithFeatureState(features::kAutofillOffNoServerData,
+                                       flag_enabled);
+
+  FormData form;
+  form.origin = GURL("http://foo.com");
+  FormFieldData field;
+  field.form_control_type = "text";
+  field.max_length = 10000;
+  field.should_autocomplete = false;
+
+  // Autocomplete Off, with server data.
+  field.label = ASCIIToUTF16("First Name");
+  field.name = ASCIIToUTF16("firstName");
+  form.fields.push_back(field);
+
+  // Autocomplete Off, without server data.
+  field.label = ASCIIToUTF16("Last Name");
+  field.name = ASCIIToUTF16("lastName");
+  form.fields.push_back(field);
+
+  // Autocomplete On, with server data.
+  field.should_autocomplete = true;
+  field.label = ASCIIToUTF16("Address");
+  field.name = ASCIIToUTF16("address");
+  form.fields.push_back(field);
+
+  // Autocomplete On, without server data.
+  field.label = ASCIIToUTF16("Country");
+  field.name = ASCIIToUTF16("country");
+  form.fields.push_back(field);
+
+  AutofillQueryResponseContents response;
+  response.add_field()->set_overall_type_prediction(NAME_FIRST);
+  response.add_field()->set_overall_type_prediction(NO_SERVER_DATA);
+  response.add_field()->set_overall_type_prediction(NO_SERVER_DATA);
+  response.add_field()->set_overall_type_prediction(NO_SERVER_DATA);
+
+  std::string response_string;
+  ASSERT_TRUE(response.SerializeToString(&response_string));
+
+  FormStructure form_structure(form);
+
+  // Will identify the sections based on the heuristics types.
+  form_structure.DetermineHeuristicTypes();
+
+  std::vector<FormStructure*> forms;
+  forms.push_back(&form_structure);
+
+  // Will call RationalizeFieldTypePredictions
+  FormStructure::ParseQueryResponse(response_string, forms, nullptr);
+
+  ASSERT_EQ(1U, forms.size());
+  ASSERT_EQ(4U, forms[0]->field_count());
+
+  // Only NAME_LAST should be affected by the flag.
+  EXPECT_EQ(flag_enabled ? UNKNOWN_TYPE : NAME_LAST,
+            forms[0]->field(1)->Type().GetStorableType());
+
+  EXPECT_EQ(NAME_FIRST, forms[0]->field(0)->Type().GetStorableType());
+  EXPECT_EQ(ADDRESS_HOME_LINE1, forms[0]->field(2)->Type().GetStorableType());
+  EXPECT_EQ(ADDRESS_HOME_COUNTRY, forms[0]->field(3)->Type().GetStorableType());
+}
+
 TEST_F(FormStructureTest, AllowBigForms) {
   FormData form;
   form.origin = GURL("http://foo.com");
diff --git a/components/autofill/core/common/autofill_features.cc b/components/autofill/core/common/autofill_features.cc
index 0a175f96..634a03d 100644
--- a/components/autofill/core/common/autofill_features.cc
+++ b/components/autofill/core/common/autofill_features.cc
@@ -70,6 +70,9 @@
 const base::Feature kAutofillEnableCompanyName{
     "AutofillEnableCompanyName", base::FEATURE_ENABLED_BY_DEFAULT};
 
+const base::Feature kAutofillOffNoServerData{"AutofillOffNoServerData",
+                                             base::FEATURE_DISABLED_BY_DEFAULT};
+
 // When enabled, autofill server will override field types with rater
 // consensus data before returning to client.
 const base::Feature kAutofillOverrideWithRaterConsensus{
diff --git a/components/autofill/core/common/autofill_features.h b/components/autofill/core/common/autofill_features.h
index a148569..fbf0e97 100644
--- a/components/autofill/core/common/autofill_features.h
+++ b/components/autofill/core/common/autofill_features.h
@@ -40,6 +40,7 @@
 extern const base::Feature kAutofillManualFallback;
 extern const base::Feature kAutofillManualFallbackPhaseTwo;
 extern const base::Feature kAutofillMetadataUploads;
+extern const base::Feature kAutofillOffNoServerData;
 extern const base::Feature kAutofillOverrideWithRaterConsensus;
 extern const base::Feature kAutofillPreferServerNamePredictions;
 extern const base::Feature kAutofillProfileServerValidation;
diff --git a/components/browser_sync/profile_sync_components_factory_impl.cc b/components/browser_sync/profile_sync_components_factory_impl.cc
index 53961dc..5de561a 100644
--- a/components/browser_sync/profile_sync_components_factory_impl.cc
+++ b/components/browser_sync/profile_sync_components_factory_impl.cc
@@ -396,8 +396,7 @@
         syncer::READING_LIST));
   }
 
-  if (!disabled_types.Has(syncer::USER_EVENTS) &&
-      FeatureList::IsEnabled(switches::kSyncUserEvents)) {
+  if (!disabled_types.Has(syncer::USER_EVENTS)) {
     controllers.push_back(CreateModelTypeControllerForModelRunningOnUIThread(
         syncer::USER_EVENTS));
   }
diff --git a/components/cronet/android/BUILD.gn b/components/cronet/android/BUILD.gn
index 94e73a7..087b448 100644
--- a/components/cronet/android/BUILD.gn
+++ b/components/cronet/android/BUILD.gn
@@ -55,12 +55,6 @@
   srcjar_deps = [ ":cronet_jni_registration" ]
 }
 
-java_cpp_enum("effective_connection_type_java") {
-  sources = [
-    "//net/nqe/effective_connection_type.h",
-  ]
-}
-
 java_cpp_enum("rtt_throughput_values_java") {
   sources = [
     "//net/nqe/network_quality.h",
@@ -260,11 +254,11 @@
 }
 
 cronet_impl_common_java_srcjar_deps = [
-  ":effective_connection_type_java",
   ":http_cache_type_java",
   ":integrated_mode_state",
   ":load_states_list",
   ":rtt_throughput_values_java",
+  "//net:effective_connection_type_java",
 ]
 
 cronet_impl_common_java_deps_to_package =
diff --git a/components/domain_reliability/bake_in_configs.py b/components/domain_reliability/bake_in_configs.py
index d3d5f6e6..cf2e8b4 100755
--- a/components/domain_reliability/bake_in_configs.py
+++ b/components/domain_reliability/bake_in_configs.py
@@ -8,6 +8,7 @@
 encodes their contents as an array of C strings that gets compiled in to Chrome
 and loaded at runtime."""
 
+from __future__ import print_function
 
 import ast
 import json
@@ -532,13 +533,13 @@
   opts, args = parser.parse_args()
 
   if not opts.output:
-    print >> sys.stderr, "--output argument required"
+    print("--output argument required", file=sys.stderr)
     return 1
 
   if opts.gypi_file:
     # .gypi-style input.
     if not opts.gypi_relative_to:
-      print >> sys.stderr, "--gypi-relative-to is required with --gypi-file"
+      print("--gypi-relative-to is required with --gypi-file", file=sys.stderr)
       return 1
     json_files = read_json_files_from_gypi(opts.gypi_file)
     json_files = [ os.path.join(opts.gypi_relative_to, f) for f in json_files ]
@@ -547,7 +548,7 @@
     # Regular file list input.
     json_files = read_json_files_from_file(opts.file_list)
   else:
-    print >> sys.stderr, "Either --file-list or --gypi-file is required."
+    print("Either --file-list or --gypi-file is required.", file=sys.stderr)
     return 1
 
   cpp_code = CC_HEADER
@@ -558,18 +559,19 @@
       json_text = f.read()
     try:
       config = json.loads(json_text)
-    except ValueError, e:
-      print >> sys.stderr, "%s: error parsing JSON: %s" % (json_file, e)
+    except ValueError as e:
+      print("%s: error parsing JSON: %s" % (json_file, e), file=sys.stderr)
       found_invalid_config = True
       continue
     if 'origin' not in config:
-      print >> sys.stderr, '%s: no origin found' % json_file
+      print('%s: no origin found' % json_file, file=sys.stderr)
       found_invalid_config = True
       continue
     origin = config['origin']
     if not origin_is_whitelisted(origin):
-      print >> sys.stderr, ('%s: origin "%s" not in whitelist' %
-                            (json_file, origin))
+      print(
+          '%s: origin "%s" not in whitelist' % (json_file, origin),
+          file=sys.stderr)
       found_invalid_config = True
       continue
 
@@ -585,7 +587,7 @@
   if found_invalid_config:
     return 1
 
-  with open(opts.output, 'wb') as f:
+  with open(opts.output, 'w') as f:
     f.write(cpp_code)
 
   return 0
diff --git a/components/metrics/file_metrics_provider.cc b/components/metrics/file_metrics_provider.cc
index ae677948..938cc89 100644
--- a/components/metrics/file_metrics_provider.cc
+++ b/components/metrics/file_metrics_provider.cc
@@ -636,13 +636,10 @@
     SystemProfileProto* system_profile_proto,
     base::HistogramSnapshotManager* snapshot_manager) {
   RecordEmbeddedProfileResult(EMBEDDED_PROFILE_ATTEMPT);
-  base::Time start_time = base::Time::Now();
   if (PersistentSystemProfile::GetSystemProfile(
           *source->allocator->memory_allocator(), system_profile_proto)) {
     system_profile_proto->mutable_stability()->set_from_previous_run(true);
     RecordHistogramSnapshotsFromSource(snapshot_manager, source);
-    UMA_HISTOGRAM_TIMES("UMA.FileMetricsProvider.EmbeddedProfile.RecordTime",
-                        base::Time::Now() - start_time);
     RecordEmbeddedProfileResult(EMBEDDED_PROFILE_FOUND);
 
     if (system_profile_proto->hardware().has_cpu()) {
diff --git a/components/omnibox/common/omnibox_features.cc b/components/omnibox/common/omnibox_features.cc
index 9486d34..1d927bd 100644
--- a/components/omnibox/common/omnibox_features.cc
+++ b/components/omnibox/common/omnibox_features.cc
@@ -171,8 +171,13 @@
 // Feature used for showing the URL suggestion favicons as a UI experiment,
 // currently only used on desktop platforms.
 const base::Feature kUIExperimentShowSuggestionFavicons{
-    "OmniboxUIExperimentShowSuggestionFavicons",
-    base::FEATURE_ENABLED_BY_DEFAULT};
+  "OmniboxUIExperimentShowSuggestionFavicons",
+#if defined(OS_ANDROID)
+      base::FEATURE_DISABLED_BY_DEFAULT
+#else
+      base::FEATURE_ENABLED_BY_DEFAULT
+#endif
+};
 
 // Feature used to always swap the title and URL.
 const base::Feature kUIExperimentSwapTitleAndUrl{
diff --git a/components/policy/resources/policy_templates.json b/components/policy/resources/policy_templates.json
index f4bed0c..65423ae1 100644
--- a/components/policy/resources/policy_templates.json
+++ b/components/policy/resources/policy_templates.json
@@ -1981,8 +1981,8 @@
         'type': 'string',
         'enum': [
           'any',
-          'secure',
-          'unsecure',
+          'pin',
+          'no_pin',
         ],
       },
       'items': [
@@ -1992,24 +1992,24 @@
           'caption': '''Allow printing both with and without PIN''',
         },
         {
-          'name': 'secure',
-          'value': 'secure',
+          'name': 'pin',
+          'value': 'pin',
           'caption': '''Allow printing only with PIN''',
         },
         {
-          'name': 'unsecure',
-          'value': 'unsecure',
+          'name': 'no_pin',
+          'value': 'no_pin',
           'caption': '''Allow printing only without PIN''',
         },
       ],
-      'supported_on': ['chrome_os:74-'],
+      'supported_on': ['chrome_os:75-'],
       'future': True,
       'features': {
         'can_be_recommended': False,
         'dynamic_refresh': True,
         'per_profile': True,
       },
-      'example_value': 'secure',
+      'example_value': 'pin',
       'id': 525,
       'caption': '''Restrict PIN printing mode''',
       'tags': [],
@@ -2128,30 +2128,30 @@
       'schema': {
         'type': 'string',
         'enum': [
-          'secure',
-          'unsecure',
+          'pin',
+          'no_pin',
         ],
       },
       'items': [
         {
-          'name': 'secure',
-          'value': 'secure',
+          'name': 'pin',
+          'value': 'pin',
           'caption': '''Enable PIN printing by default''',
         },
         {
-          'name': 'unsecure',
-          'value': 'unsecure',
+          'name': 'no_pin',
+          'value': 'no_pin',
           'caption': '''Disable PIN printing by default''',
         },
       ],
-      'supported_on': ['chrome_os:74-'],
+      'supported_on': ['chrome_os:75-'],
       'future': True,
       'features': {
         'can_be_recommended': False,
         'dynamic_refresh': True,
         'per_profile': True,
       },
-      'example_value': 'secure',
+      'example_value': 'pin',
       'id': 526,
       'caption': '''Default PIN printing mode''',
       'tags': [],
@@ -15085,6 +15085,7 @@
         'dynamic_refresh': False,
         'per_profile': True,
       },
+      'default_for_enterprise_users': True,
       'example_value': False,
       'id': 533,
       'caption': '''Allows a page to show popups during its unloading''',
diff --git a/components/printing/renderer/print_render_frame_helper.cc b/components/printing/renderer/print_render_frame_helper.cc
index f0e785d..eb717d8d 100644
--- a/components/printing/renderer/print_render_frame_helper.cc
+++ b/components/printing/renderer/print_render_frame_helper.cc
@@ -789,8 +789,7 @@
       blink::WebTreeScopeType scope,
       const blink::WebString& name,
       const blink::WebString& fallback_name,
-      blink::WebSandboxFlags sandbox_flags,
-      const blink::ParsedFeaturePolicy& container_policy,
+      const blink::FramePolicy& frame_policy,
       const blink::WebFrameOwnerProperties& frame_owner_properties,
       blink::FrameOwnerElementType owner_type) override;
   void FrameDetached(DetachType detach_type) override;
@@ -970,8 +969,7 @@
     blink::WebTreeScopeType scope,
     const blink::WebString& name,
     const blink::WebString& fallback_name,
-    blink::WebSandboxFlags sandbox_flags,
-    const blink::ParsedFeaturePolicy& container_policy,
+    const blink::FramePolicy& frame_policy,
     const blink::WebFrameOwnerProperties& frame_owner_properties,
     blink::FrameOwnerElementType frame_owner_type) {
   // This is called when printing a selection and when this selection contains
diff --git a/components/search_engines/prepopulated_engines.json b/components/search_engines/prepopulated_engines.json
index 9d7af2b..877048a 100644
--- a/components/search_engines/prepopulated_engines.json
+++ b/components/search_engines/prepopulated_engines.json
@@ -28,7 +28,7 @@
     // Increment this if you change the data in ways that mean users with
     // existing data should get a new version. Otherwise, existing data may
     // continue to be used and updates made here will not always appear.
-    "kCurrentDataVersion": 111
+    "kCurrentDataVersion": 112
   },
 
   // The following engines are included in country lists and are added to the
@@ -101,6 +101,7 @@
       "favicon_url": "https://duckduckgo.com/favicon.ico",
       "search_url": "https://duckduckgo.com/?q={searchTerms}",
       "suggest_url": "https://duckduckgo.com/ac/?q={searchTerms}&type=list",
+      "new_tab_url": "https://duckduckgo.com/chrome_newtab",
       "type": "SEARCH_ENGINE_DUCKDUCKGO",
       "id": 92
     },
diff --git a/components/signin/core/browser/chrome_connected_header_helper.cc b/components/signin/core/browser/chrome_connected_header_helper.cc
index fa9c1b3..142c31b 100644
--- a/components/signin/core/browser/chrome_connected_header_helper.cc
+++ b/components/signin/core/browser/chrome_connected_header_helper.cc
@@ -22,6 +22,8 @@
 
 namespace {
 
+const char kConsistencyEnabledByDefaultAttrName[] =
+    "consistency_enabled_by_default";
 const char kContinueUrlAttrName[] = "continue_url";
 const char kEmailAttrName[] = "email";
 const char kEnableAccountConsistencyAttrName[] = "enable_account_consistency";
@@ -167,20 +169,23 @@
     const GURL& url,
     const std::string& account_id,
     int profile_mode_mask) {
-// If we are on mobile, an empty |account_id| corresponds to the user not signed
-// into Sync. Do not enforce account consistency, unless Mice is enabled on
-// Android.
+#if defined(OS_ANDROID)
+  bool is_mice_enabled = base::FeatureList::IsEnabled(kMiceFeature);
+#else
+  bool is_mice_enabled = false;
+#endif
+
+// If we are on mobile or desktop, an empty |account_id| corresponds to the user
+// not signed into Sync. Do not enforce account consistency, unless Mice is
+// enabled on Android.
 // On Chrome OS, an empty |account_id| corresponds to Public Sessions, Guest
 // Sessions and Active Directory logins. Guest Sessions have already been
 // filtered upstream and we want to enforce account consistency in Public
 // Sessions and Active Directory logins.
-#if defined(OS_ANDROID)
-  if (account_id.empty() && !base::FeatureList::IsEnabled(kMiceFeature))
+#if !defined(OS_CHROMEOS)
+  if (account_id.empty() && !is_mice_enabled)
     return std::string();
-#elif !defined(OS_CHROMEOS)
-  if (account_id.empty())
-    return std::string();
-#endif
+#endif  // !defined(OS_CHROMEOS)
 
   std::vector<std::string> parts;
   if (!account_id.empty() &&
@@ -196,6 +201,9 @@
       account_consistency_ == AccountConsistencyMethod::kMirror;
   parts.push_back(base::StringPrintf("%s=%s", kEnableAccountConsistencyAttrName,
                                      is_mirror_enabled ? "true" : "false"));
+  parts.push_back(base::StringPrintf("%s=%s",
+                                     kConsistencyEnabledByDefaultAttrName,
+                                     is_mice_enabled ? "true" : "false"));
 
   return base::JoinString(parts, is_header_request ? "," : ":");
 }
diff --git a/components/signin/core/browser/signin_error_controller.cc b/components/signin/core/browser/signin_error_controller.cc
index 21c5c29f..9b2e0b9 100644
--- a/components/signin/core/browser/signin_error_controller.cc
+++ b/components/signin/core/browser/signin_error_controller.cc
@@ -26,21 +26,68 @@
 }
 
 void SigninErrorController::Update() {
-  GoogleServiceAuthError::State prev_state = auth_error_.state();
-  std::string prev_account_id = error_account_id_;
+  const GoogleServiceAuthError::State prev_error_state = auth_error_.state();
+  const std::string prev_account_id = error_account_id_;
   bool error_changed = false;
 
+  const std::string& primary_account_id =
+      identity_manager_->GetPrimaryAccountId();
+
+  if (identity_manager_->HasAccountWithRefreshTokenInPersistentErrorState(
+          primary_account_id)) {
+    // Prioritize Primary Account errors over everything else.
+    auth_error_ = identity_manager_->GetErrorStateOfRefreshTokenForAccount(
+        primary_account_id);
+    DCHECK(auth_error_.IsPersistentError());
+    error_account_id_ = primary_account_id;
+    error_changed = true;
+  } else if (account_mode_ != AccountMode::PRIMARY_ACCOUNT) {
+    // Additionally, check for Secondary Account errors, if we are not in
+    // |AccountMode::PRIMARY_ACCOUNT| mode.
+    error_changed = UpdateSecondaryAccountErrors(
+        primary_account_id, prev_account_id, prev_error_state);
+  }
+
+  if (!error_changed && prev_error_state != GoogleServiceAuthError::NONE) {
+    // No provider reported an error, so clear the error we have now.
+    auth_error_ = GoogleServiceAuthError::AuthErrorNone();
+    error_account_id_.clear();
+    error_changed = true;
+  }
+
+  if (!error_changed)
+    return;
+
+  if (auth_error_.state() == prev_error_state &&
+      error_account_id_ == prev_account_id) {
+    // Only fire notification if the auth error state or account were updated.
+    return;
+  }
+
+  signin_metrics::LogAuthError(auth_error_);
+  for (auto& observer : observer_list_)
+    observer.OnErrorChanged();
+}
+
+bool SigninErrorController::UpdateSecondaryAccountErrors(
+    const std::string& primary_account_id,
+    const std::string& prev_account_id,
+    const GoogleServiceAuthError::State& prev_error_state) {
+  // This method should not have been called if we are in
+  // |AccountMode::PRIMARY_ACCOUNT|.
+  DCHECK_NE(AccountMode::PRIMARY_ACCOUNT, account_mode_);
+
   // Find an error among the status providers. If |auth_error_| has an
   // actionable error state and some provider exposes a similar error and
   // account id, use that error. Otherwise, just take the first actionable
   // error we find.
+  bool error_changed = false;
   for (const AccountInfo& account_info :
        identity_manager_->GetAccountsWithRefreshTokens()) {
     std::string account_id = account_info.account_id;
 
-    // In PRIMARY_ACCOUNT mode, ignore all secondary accounts.
-    if (account_mode_ == AccountMode::PRIMARY_ACCOUNT &&
-        (account_id != identity_manager_->GetPrimaryAccountId())) {
+    // Ignore the Primary Account. We are only interested in Secondary Accounts.
+    if (account_id == primary_account_id) {
       continue;
     }
 
@@ -55,12 +102,12 @@
     DCHECK(error.IsPersistentError());
 
     // Prioritize this error if it matches the previous |auth_error_|.
-    if (error.state() == prev_state && account_id == prev_account_id) {
+    if (error.state() == prev_error_state && account_id == prev_account_id) {
       // The previous error for the previous account still exists. This error is
       // preferred to avoid UI churn, so |auth_error_| and |error_account_id_|
       // must be updated to match the previous state. This is needed in case
-      // |auth_error_| and |error_account_id_| were updated to other values in
-      // a previous iteration via the if statement below.
+      // |auth_error_| and |error_account_id_| were updated to other values in a
+      // previous iteration via the if statement below.
       auth_error_ = error;
       error_account_id_ = account_id;
       error_changed = true;
@@ -76,25 +123,7 @@
     }
   }
 
-  if (!error_changed && prev_state != GoogleServiceAuthError::NONE) {
-    // No provider reported an error, so clear the error we have now.
-    auth_error_ = GoogleServiceAuthError::AuthErrorNone();
-    error_account_id_.clear();
-    error_changed = true;
-  }
-
-  if (!error_changed)
-    return;
-
-  if (auth_error_.state() == prev_state &&
-      error_account_id_ == prev_account_id) {
-    // Only fire notification if the auth error state or account were updated.
-    return;
-  }
-
-  signin_metrics::LogAuthError(auth_error_);
-  for (auto& observer : observer_list_)
-    observer.OnErrorChanged();
+  return error_changed;
 }
 
 bool SigninErrorController::HasError() const {
diff --git a/components/signin/core/browser/signin_error_controller.h b/components/signin/core/browser/signin_error_controller.h
index 6edab303..3111119 100644
--- a/components/signin/core/browser/signin_error_controller.h
+++ b/components/signin/core/browser/signin_error_controller.h
@@ -64,6 +64,17 @@
   // Invoked when the auth status has changed.
   void Update();
 
+  // Checks for Secondary Account errors and updates |auth_error_| and
+  // |error_account_id_| accordingly. Does not do anything if no Secondary
+  // Account has any error. Returns true if an error was found in a Secondary
+  // Account, false otherwise.
+  // Note: This function must not be called if |account_mode_| is
+  // |AccountMode::PRIMARY_ACCOUNT|.
+  bool UpdateSecondaryAccountErrors(
+      const std::string& primary_account_id,
+      const std::string& prev_account_id,
+      const GoogleServiceAuthError::State& prev_error_state);
+
   // identity::IdentityManager::Observer:
   void OnEndBatchOfRefreshTokenStateChanges() override;
   void OnErrorStateOfRefreshTokenUpdatedForAccount(
diff --git a/components/signin/core/browser/signin_error_controller_unittest.cc b/components/signin/core/browser/signin_error_controller_unittest.cc
index 2b88b04cf..90b3539 100644
--- a/components/signin/core/browser/signin_error_controller_unittest.cc
+++ b/components/signin/core/browser/signin_error_controller_unittest.cc
@@ -19,8 +19,9 @@
 
 namespace {
 
-static const char kTestEmail[] = "me@test.com";
-static const char kOtherTestEmail[] = "you@test.com";
+constexpr char kPrimaryAccountEmail[] = "primary@example.com";
+constexpr char kTestEmail[] = "me@test.com";
+constexpr char kOtherTestEmail[] = "you@test.com";
 
 class MockSigninErrorControllerObserver
     : public SigninErrorController::Observer {
@@ -285,3 +286,99 @@
       test_account_id, GoogleServiceAuthError(GoogleServiceAuthError::NONE));
   ASSERT_FALSE(error_controller.HasError());
 }
+
+TEST(SigninErrorControllerTest,
+     PrimaryAccountErrorsArePreferredToSecondaryAccountErrors) {
+  base::test::ScopedTaskEnvironment task_environment;
+  identity::IdentityTestEnvironment identity_test_env;
+
+  AccountInfo primary_account_info =
+      identity_test_env.MakePrimaryAccountAvailable(kPrimaryAccountEmail);
+  std::string secondary_account_id =
+      identity_test_env.MakeAccountAvailable(kTestEmail).account_id;
+  SigninErrorController error_controller(
+      SigninErrorController::AccountMode::ANY_ACCOUNT,
+      identity_test_env.identity_manager());
+  ASSERT_FALSE(error_controller.HasError());
+
+  // Set an error for the Secondary Account.
+  identity_test_env.UpdatePersistentErrorOfRefreshTokenForAccount(
+      secondary_account_id,
+      GoogleServiceAuthError(GoogleServiceAuthError::INVALID_GAIA_CREDENTIALS));
+  ASSERT_EQ(GoogleServiceAuthError::INVALID_GAIA_CREDENTIALS,
+            error_controller.auth_error().state());
+  ASSERT_EQ(secondary_account_id, error_controller.error_account_id());
+
+  // Set an error for the Primary Account. This should override the previous
+  // error.
+  identity_test_env.UpdatePersistentErrorOfRefreshTokenForAccount(
+      primary_account_info.account_id,
+      GoogleServiceAuthError(GoogleServiceAuthError::INVALID_GAIA_CREDENTIALS));
+  ASSERT_EQ(GoogleServiceAuthError::INVALID_GAIA_CREDENTIALS,
+            error_controller.auth_error().state());
+  ASSERT_EQ(primary_account_info.account_id,
+            error_controller.error_account_id());
+
+  // Clear the Primary Account error. This should cause the Secondary Account
+  // error to be returned again.
+  identity_test_env.UpdatePersistentErrorOfRefreshTokenForAccount(
+      primary_account_info.account_id,
+      GoogleServiceAuthError(GoogleServiceAuthError::NONE));
+  ASSERT_EQ(GoogleServiceAuthError::INVALID_GAIA_CREDENTIALS,
+            error_controller.auth_error().state());
+  ASSERT_EQ(secondary_account_id, error_controller.error_account_id());
+
+  // Clear the Secondary Account error too. All errors should be gone now.
+  identity_test_env.UpdatePersistentErrorOfRefreshTokenForAccount(
+      secondary_account_id,
+      GoogleServiceAuthError(GoogleServiceAuthError::NONE));
+  ASSERT_FALSE(error_controller.HasError());
+}
+
+TEST(SigninErrorControllerTest, PrimaryAccountErrorsAreSticky) {
+  base::test::ScopedTaskEnvironment task_environment;
+  identity::IdentityTestEnvironment identity_test_env;
+
+  AccountInfo primary_account_info =
+      identity_test_env.MakePrimaryAccountAvailable(kPrimaryAccountEmail);
+  std::string secondary_account_id =
+      identity_test_env.MakeAccountAvailable(kTestEmail).account_id;
+  SigninErrorController error_controller(
+      SigninErrorController::AccountMode::ANY_ACCOUNT,
+      identity_test_env.identity_manager());
+  ASSERT_FALSE(error_controller.HasError());
+
+  // Set an error for the Primary Account.
+  identity_test_env.UpdatePersistentErrorOfRefreshTokenForAccount(
+      primary_account_info.account_id,
+      GoogleServiceAuthError(GoogleServiceAuthError::INVALID_GAIA_CREDENTIALS));
+  ASSERT_EQ(GoogleServiceAuthError::INVALID_GAIA_CREDENTIALS,
+            error_controller.auth_error().state());
+  ASSERT_EQ(primary_account_info.account_id,
+            error_controller.error_account_id());
+
+  // Set an error for the Secondary Account. The Primary Account error should
+  // stick.
+  identity_test_env.UpdatePersistentErrorOfRefreshTokenForAccount(
+      secondary_account_id,
+      GoogleServiceAuthError(GoogleServiceAuthError::INVALID_GAIA_CREDENTIALS));
+  ASSERT_EQ(GoogleServiceAuthError::INVALID_GAIA_CREDENTIALS,
+            error_controller.auth_error().state());
+  ASSERT_EQ(primary_account_info.account_id,
+            error_controller.error_account_id());
+
+  // Clear the Primary Account error. This should cause the Secondary Account
+  // error to be returned again.
+  identity_test_env.UpdatePersistentErrorOfRefreshTokenForAccount(
+      primary_account_info.account_id,
+      GoogleServiceAuthError(GoogleServiceAuthError::NONE));
+  ASSERT_EQ(GoogleServiceAuthError::INVALID_GAIA_CREDENTIALS,
+            error_controller.auth_error().state());
+  ASSERT_EQ(secondary_account_id, error_controller.error_account_id());
+
+  // Clear the Secondary Account error too. All errors should be gone now.
+  identity_test_env.UpdatePersistentErrorOfRefreshTokenForAccount(
+      secondary_account_id,
+      GoogleServiceAuthError(GoogleServiceAuthError::NONE));
+  ASSERT_FALSE(error_controller.HasError());
+}
diff --git a/components/signin/core/browser/signin_header_helper_unittest.cc b/components/signin/core/browser/signin_header_helper_unittest.cc
index ac7493a..a8935aa 100644
--- a/components/signin/core/browser/signin_header_helper_unittest.cc
+++ b/components/signin/core/browser/signin_header_helper_unittest.cc
@@ -133,9 +133,11 @@
 TEST_F(SigninHeaderHelperTest, TestMirrorRequestNoAccountIdChromeOS) {
   account_consistency_ = AccountConsistencyMethod::kMirror;
   CheckMirrorHeaderRequest(GURL("https://docs.google.com"), "",
-                           "mode=0,enable_account_consistency=true");
+                           "mode=0,enable_account_consistency=true,"
+                           "consistency_enabled_by_default=false");
   CheckMirrorCookieRequest(GURL("https://docs.google.com"), "",
-                           "mode=0:enable_account_consistency=true");
+                           "mode=0:enable_account_consistency=true:"
+                           "consistency_enabled_by_default=false");
 }
 #else  // !defined(OS_CHROMEOS)
 // Tests that no Mirror request is returned when the user is not signed in (no
@@ -158,9 +160,11 @@
   scoped_feature_list.InitAndEnableFeature(kMiceFeature);
   account_consistency_ = AccountConsistencyMethod::kMirror;
   CheckMirrorHeaderRequest(GURL("https://docs.google.com"), "",
-                           "mode=0,enable_account_consistency=true");
+                           "mode=0,enable_account_consistency=true,"
+                           "consistency_enabled_by_default=true");
   CheckMirrorCookieRequest(GURL("https://docs.google.com"), "",
-                           "mode=0:enable_account_consistency=true");
+                           "mode=0:enable_account_consistency=true:"
+                           "consistency_enabled_by_default=true");
 }
 #endif
 
@@ -185,9 +189,11 @@
 TEST_F(SigninHeaderHelperTest, TestMirrorRequestGoogleTLD) {
   account_consistency_ = AccountConsistencyMethod::kMirror;
   CheckMirrorHeaderRequest(GURL("https://google.fr"), "0123456789",
-                           "mode=0,enable_account_consistency=true");
+                           "mode=0,enable_account_consistency=true,"
+                           "consistency_enabled_by_default=false");
   CheckMirrorCookieRequest(GURL("https://google.de"), "0123456789",
-                           "mode=0:enable_account_consistency=true");
+                           "mode=0:enable_account_consistency=true:"
+                           "consistency_enabled_by_default=false");
 }
 
 // Tests that the Mirror request is returned when the target is the domain
@@ -195,10 +201,12 @@
 TEST_F(SigninHeaderHelperTest, TestMirrorRequestGoogleCom) {
   account_consistency_ = AccountConsistencyMethod::kMirror;
   CheckMirrorHeaderRequest(GURL("https://www.google.com"), "0123456789",
-                           "mode=0,enable_account_consistency=true");
+                           "mode=0,enable_account_consistency=true,"
+                           "consistency_enabled_by_default=false");
   CheckMirrorCookieRequest(
       GURL("https://www.google.com"), "0123456789",
-      "id=0123456789:mode=0:enable_account_consistency=true");
+      "id=0123456789:mode=0:enable_account_consistency=true:"
+      "consistency_enabled_by_default=false");
 }
 
 // Tests that no header sent when mirror account consistency is nor requested.
@@ -226,9 +234,10 @@
   AppendOrRemoveMirrorRequestHeader(
       &request_adapter, GURL(), "0123456789", account_consistency_,
       cookie_settings_.get(), PROFILE_MODE_DEFAULT);
-  CheckAccountConsistencyHeaderRequest(
-      url_request.get(), kChromeConnectedHeader,
-      "mode=0,enable_account_consistency=true");
+  CheckAccountConsistencyHeaderRequest(url_request.get(),
+                                       kChromeConnectedHeader,
+                                       "mode=0,enable_account_consistency=true,"
+                                       "consistency_enabled_by_default=false");
 }
 
 // Mirror is always enabled on Android and iOS, so these tests are only relevant
@@ -239,10 +248,12 @@
 // if account consistency is disabled.
 TEST_F(SigninHeaderHelperTest, TestMirrorRequestGaiaURL) {
   CheckMirrorHeaderRequest(GURL("https://accounts.google.com"), "0123456789",
-                           "mode=0,enable_account_consistency=false");
+                           "mode=0,enable_account_consistency=false,"
+                           "consistency_enabled_by_default=false");
   CheckMirrorCookieRequest(
       GURL("https://accounts.google.com"), "0123456789",
-      "id=0123456789:mode=0:enable_account_consistency=false");
+      "id=0123456789:mode=0:enable_account_consistency=false:"
+      "consistency_enabled_by_default=false");
 }
 
 // Tests Dice requests.
@@ -251,7 +262,9 @@
   // ChromeConnected but no Dice for Docs URLs.
   CheckDiceHeaderRequest(
       GURL("https://docs.google.com"), "0123456789",
-      "id=0123456789,mode=0,enable_account_consistency=false", "");
+      "id=0123456789,mode=0,enable_account_consistency=false,"
+      "consistency_enabled_by_default=false",
+      "");
 
   // ChromeConnected and Dice for Gaia URLs.
   // Sync disabled.
@@ -259,7 +272,8 @@
   ASSERT_FALSE(client_id.empty());
   CheckDiceHeaderRequest(
       GURL("https://accounts.google.com"), "0123456789",
-      "mode=0,enable_account_consistency=false",
+      "mode=0,enable_account_consistency=false,"
+      "consistency_enabled_by_default=false",
       base::StringPrintf(
           "version=%s,client_id=%s,device_id=DeviceID,signin_mode=all_accounts,"
           "signout_mode=show_confirmation",
@@ -269,7 +283,8 @@
   sync_enabled_ = true;
   CheckDiceHeaderRequest(
       GURL("https://accounts.google.com"), "0123456789",
-      "mode=0,enable_account_consistency=false",
+      "mode=0,enable_account_consistency=false,"
+      "consistency_enabled_by_default=false",
       base::StringPrintf("version=%s,client_id=%s,device_id=DeviceID,"
                          "sync_account_id=0123456789,signin_mode=all_accounts,"
                          "signout_mode=show_confirmation",
@@ -299,7 +314,9 @@
 TEST_F(SigninHeaderHelperTest, TestNoDiceRequestWhenDisabled) {
   account_consistency_ = AccountConsistencyMethod::kMirror;
   CheckDiceHeaderRequest(GURL("https://accounts.google.com"), "0123456789",
-                         "mode=0,enable_account_consistency=true", "");
+                         "mode=0,enable_account_consistency=true,"
+                         "consistency_enabled_by_default=false",
+                         "");
 }
 
 TEST_F(SigninHeaderHelperTest, TestDiceEmptyDeviceID) {
@@ -311,7 +328,8 @@
 
   CheckDiceHeaderRequest(
       GURL("https://accounts.google.com"), "0123456789",
-      "mode=0,enable_account_consistency=false",
+      "mode=0,enable_account_consistency=false,"
+      "consistency_enabled_by_default=false",
       base::StringPrintf("version=%s,client_id=%s,signin_mode=all_accounts,"
                          "signout_mode=no_confirmation",
                          kDiceProtocolVersion, client_id.c_str()));
@@ -327,7 +345,8 @@
   // No signout confirmation by default.
   CheckDiceHeaderRequest(
       GURL("https://accounts.google.com"), "0123456789",
-      "mode=0,enable_account_consistency=false",
+      "mode=0,enable_account_consistency=false,"
+      "consistency_enabled_by_default=false",
       base::StringPrintf(
           "version=%s,client_id=%s,device_id=DeviceID,signin_mode=all_accounts,"
           "signout_mode=no_confirmation",
@@ -337,7 +356,8 @@
   account_consistency_ = AccountConsistencyMethod::kDice;
   CheckDiceHeaderRequest(
       GURL("https://accounts.google.com"), "0123456789",
-      "mode=0,enable_account_consistency=false",
+      "mode=0,enable_account_consistency=false,"
+      "consistency_enabled_by_default=false",
       base::StringPrintf(
           "version=%s,client_id=%s,device_id=DeviceID,signin_mode=all_accounts,"
           "signout_mode=show_confirmation",
@@ -349,19 +369,23 @@
 TEST_F(SigninHeaderHelperTest, TestMirrorRequestDrive) {
   CheckMirrorHeaderRequest(
       GURL("https://docs.google.com/document"), "0123456789",
-      "id=0123456789,mode=0,enable_account_consistency=false");
+      "id=0123456789,mode=0,enable_account_consistency=false,"
+      "consistency_enabled_by_default=false");
   CheckMirrorCookieRequest(
       GURL("https://drive.google.com/drive"), "0123456789",
-      "id=0123456789:mode=0:enable_account_consistency=false");
+      "id=0123456789:mode=0:enable_account_consistency=false:"
+      "consistency_enabled_by_default=false");
 
   // Enable Account Consistency will override the disable.
   account_consistency_ = AccountConsistencyMethod::kMirror;
   CheckMirrorHeaderRequest(
       GURL("https://docs.google.com/document"), "0123456789",
-      "id=0123456789,mode=0,enable_account_consistency=true");
+      "id=0123456789,mode=0,enable_account_consistency=true,"
+      "consistency_enabled_by_default=false");
   CheckMirrorCookieRequest(
       GURL("https://drive.google.com/drive"), "0123456789",
-      "id=0123456789:mode=0:enable_account_consistency=true");
+      "id=0123456789:mode=0:enable_account_consistency=true:"
+      "consistency_enabled_by_default=false");
 }
 
 TEST_F(SigninHeaderHelperTest, TestDiceInvalidResponseParams) {
diff --git a/components/sync/BUILD.gn b/components/sync/BUILD.gn
index 000c7425..0f22c20 100644
--- a/components/sync/BUILD.gn
+++ b/components/sync/BUILD.gn
@@ -563,8 +563,6 @@
     "user_events/fake_user_event_service.h",
     "user_events/no_op_user_event_service.cc",
     "user_events/no_op_user_event_service.h",
-    "user_events/trial_recorder.cc",
-    "user_events/trial_recorder.h",
     "user_events/user_event_service.h",
     "user_events/user_event_service_impl.cc",
     "user_events/user_event_service_impl.h",
@@ -961,7 +959,6 @@
     "syncable/syncable_enum_conversions_unittest.cc",
     "syncable/syncable_id_unittest.cc",
     "syncable/syncable_unittest.cc",
-    "user_events/trial_recorder_unittest.cc",
     "user_events/user_event_service_impl_unittest.cc",
     "user_events/user_event_sync_bridge_unittest.cc",
   ]
diff --git a/components/sync/driver/glue/sync_engine_impl.cc b/components/sync/driver/glue/sync_engine_impl.cc
index 71922ec..6798351 100644
--- a/components/sync/driver/glue/sync_engine_impl.cc
+++ b/components/sync/driver/glue/sync_engine_impl.cc
@@ -65,6 +65,10 @@
                                 std::move(params)));
 }
 
+bool SyncEngineImpl::IsInitialized() const {
+  return initialized_;
+}
+
 void SyncEngineImpl::TriggerRefresh(const ModelTypeSet& types) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   sync_task_runner_->PostTask(
@@ -215,13 +219,13 @@
 }
 
 SyncEngineImpl::Status SyncEngineImpl::GetDetailedStatus() {
-  DCHECK(initialized());
+  DCHECK(IsInitialized());
   return core_->sync_manager()->GetDetailedStatus();
 }
 
 void SyncEngineImpl::HasUnsyncedItemsForTest(
     base::OnceCallback<void(bool)> cb) const {
-  DCHECK(initialized());
+  DCHECK(IsInitialized());
   base::PostTaskAndReplyWithResult(
       sync_task_runner_.get(), FROM_HERE,
       base::BindOnce(&SyncBackendHostCore::HasUnsyncedItemsForTest, core_),
@@ -229,7 +233,7 @@
 }
 
 void SyncEngineImpl::GetModelSafeRoutingInfo(ModelSafeRoutingInfo* out) const {
-  if (initialized()) {
+  if (IsInitialized()) {
     registrar_->GetModelSafeRoutingInfo(out);
   } else {
     NOTREACHED();
@@ -237,7 +241,7 @@
 }
 
 void SyncEngineImpl::FlushDirectory() const {
-  DCHECK(initialized());
+  DCHECK(IsInitialized());
   sync_task_runner_->PostTask(
       FROM_HERE, base::BindOnce(&SyncBackendHostCore::SaveChanges, core_));
 }
@@ -258,7 +262,7 @@
 }
 
 void SyncEngineImpl::EnableDirectoryTypeDebugInfoForwarding() {
-  DCHECK(initialized());
+  DCHECK(IsInitialized());
   sync_task_runner_->PostTask(
       FROM_HERE,
       base::BindOnce(
@@ -266,7 +270,7 @@
 }
 
 void SyncEngineImpl::DisableDirectoryTypeDebugInfoForwarding() {
-  DCHECK(initialized());
+  DCHECK(IsInitialized());
   sync_task_runner_->PostTask(
       FROM_HERE,
       base::BindOnce(
@@ -347,7 +351,7 @@
 
   // Process any changes to the datatypes we're syncing.
   // TODO(sync): add support for removing types.
-  if (initialized()) {
+  if (IsInitialized()) {
     host_->OnSyncCycleCompleted(snapshot);
   }
 }
diff --git a/components/sync/driver/glue/sync_engine_impl.h b/components/sync/driver/glue/sync_engine_impl.h
index 8bbde17e..bc0e0b6f 100644
--- a/components/sync/driver/glue/sync_engine_impl.h
+++ b/components/sync/driver/glue/sync_engine_impl.h
@@ -56,6 +56,7 @@
 
   // SyncEngine implementation.
   void Initialize(InitParams params) override;
+  bool IsInitialized() const override;
   void TriggerRefresh(const ModelTypeSet& types) override;
   void UpdateCredentials(const SyncCredentials& credentials) override;
   void InvalidateCredentials() override;
@@ -171,9 +172,6 @@
   void HandleSyncCycleCompletedOnFrontendLoop(
       const SyncCycleSnapshot& snapshot);
 
-  // For convenience, checks if initialization state is INITIALIZED.
-  bool initialized() const { return initialized_; }
-
   // Let the front end handle the actionable error event.
   void HandleActionableErrorEventOnFrontendLoop(
       const SyncProtocolError& sync_error);
diff --git a/components/sync/driver/profile_sync_service.cc b/components/sync/driver/profile_sync_service.cc
index 9395e0a8..08e89954 100644
--- a/components/sync/driver/profile_sync_service.cc
+++ b/components/sync/driver/profile_sync_service.cc
@@ -159,7 +159,6 @@
       url_loader_factory_(std::move(init_params.url_loader_factory)),
       network_connection_tracker_(init_params.network_connection_tracker),
       is_first_time_sync_configure_(false),
-      engine_initialized_(false),
       sync_disabled_by_admin_(false),
       unrecoverable_error_reason_(ERROR_REASON_UNSET),
       expect_sync_configuration_aborted_(false),
@@ -219,7 +218,7 @@
     identity_manager_->RemoveObserver(this);
   sync_prefs_.RemoveSyncPrefObserver(this);
   // Shutdown() should have been called before destruction.
-  DCHECK(!engine_initialized_);
+  DCHECK(!engine_);
 }
 
 void ProfileSyncService::Initialize() {
@@ -618,7 +617,6 @@
   // Clear various state.
   crypto_.Reset();
   expect_sync_configuration_aborted_ = false;
-  engine_initialized_ = false;
   last_snapshot_ = SyncCycleSnapshot();
   auth_manager_->ConnectionClosed();
 
@@ -698,7 +696,7 @@
     return TransportState::DISABLED;
   }
 
-  if (!engine_initialized_) {
+  if (!engine_ || !engine_->IsInitialized()) {
     switch (startup_controller_->GetState()) {
         // TODO(crbug.com/935523): If the engine is allowed to start, then we
         // should generally have kicked off the startup process already, so
@@ -810,14 +808,14 @@
 
 void ProfileSyncService::ReenableDatatype(ModelType type) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-  if (!engine_initialized_ || !data_type_manager_)
+  if (!engine_ || !engine_->IsInitialized() || !data_type_manager_)
     return;
   data_type_manager_->ReenableType(type);
 }
 
 void ProfileSyncService::ReadyForStartChanged(ModelType type) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-  if (!engine_initialized_ || !data_type_manager_)
+  if (!engine_ || !engine_->IsInitialized() || !data_type_manager_)
     return;
   data_type_manager_->ReadyForStartChanged(type);
 }
@@ -871,8 +869,6 @@
     return;
   }
 
-  engine_initialized_ = true;
-
   sync_js_controller_.AttachJsBackend(js_backend);
 
   // Initialize local device info.
@@ -971,7 +967,8 @@
 
 void ProfileSyncService::OnMigrationNeededForTypes(ModelTypeSet types) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-  DCHECK(engine_initialized_);
+  DCHECK(engine_);
+  DCHECK(engine_->IsInitialized());
   DCHECK(data_type_manager_);
 
   // Migrator must be valid, because we don't sync until it is created and this
@@ -1153,7 +1150,7 @@
 bool ProfileSyncService::QueryDetailedSyncStatusForDebugging(
     SyncStatus* result) const {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-  if (engine_ && engine_initialized_) {
+  if (engine_ && engine_->IsInitialized()) {
     *result = engine_->GetDetailedStatus();
     return true;
   }
@@ -1204,8 +1201,9 @@
 
 void ProfileSyncService::TriggerRefresh(const ModelTypeSet& types) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-  if (engine_initialized_)
+  if (engine_ && engine_->IsInitialized()) {
     engine_->TriggerRefresh(types);
+  }
 }
 
 bool ProfileSyncService::IsSignedIn() const {
@@ -1397,7 +1395,7 @@
 
 UserShare* ProfileSyncService::GetUserShare() const {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-  if (engine_ && engine_initialized_) {
+  if (engine_ && engine_->IsInitialized()) {
     return engine_->GetUserShare();
   }
   NOTREACHED();
@@ -1418,7 +1416,7 @@
     base::OnceCallback<void(bool)> cb) const {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   DCHECK(engine_);
-  DCHECK(engine_initialized_);
+  DCHECK(engine_->IsInitialized());
   engine_->HasUnsyncedItemsForTest(std::move(cb));
 }
 
@@ -1432,7 +1430,7 @@
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   auto result = std::make_unique<base::ListValue>();
 
-  if (!engine_ || !engine_initialized_) {
+  if (!engine_ || !engine_->IsInitialized()) {
     return std::move(result);
   }
 
@@ -1527,7 +1525,7 @@
 
 void ProfileSyncService::OnFirstSetupCompletePrefChange(
     bool is_first_setup_complete) {
-  if (engine_initialized_) {
+  if (engine_ && engine_->IsInitialized()) {
     ReconfigureDatatypeManager(/*bypass_setup_in_progress_check=*/false);
   }
 }
@@ -1536,7 +1534,7 @@
   if (is_sync_requested) {
     // If the Sync engine was already initialized (probably running in transport
     // mode), just reconfigure.
-    if (engine_initialized_) {
+    if (engine_ && engine_->IsInitialized()) {
       ReconfigureDatatypeManager(/*bypass_setup_in_progress_check=*/false);
     } else {
       // Otherwise try to start up. Note that there might still be other disable
@@ -1573,7 +1571,7 @@
     const std::vector<gaia::ListedAccount>& signed_in_accounts,
     const base::Closure& callback) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-  if (!engine_initialized_)
+  if (!engine_ || !engine_->IsInitialized())
     return;
 
   bool cookie_jar_mismatch = HasCookieJarMismatch(signed_in_accounts);
@@ -1617,8 +1615,8 @@
     TypeDebugInfoObserver* type_debug_info_observer) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   type_debug_info_observers_.AddObserver(type_debug_info_observer);
-  if (type_debug_info_observers_.might_have_observers() &&
-      engine_initialized_) {
+  if (type_debug_info_observers_.might_have_observers() && engine_ &&
+      engine_->IsInitialized()) {
     engine_->EnableDirectoryTypeDebugInfoForwarding();
   }
 }
@@ -1627,8 +1625,8 @@
     TypeDebugInfoObserver* type_debug_info_observer) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   type_debug_info_observers_.RemoveObserver(type_debug_info_observer);
-  if (!type_debug_info_observers_.might_have_observers() &&
-      engine_initialized_) {
+  if (!type_debug_info_observers_.might_have_observers() && engine_ &&
+      engine_->IsInitialized()) {
     engine_->DisableDirectoryTypeDebugInfoForwarding();
   }
 }
@@ -1701,7 +1699,7 @@
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
 
   // If the engine isn't initialized yet, then there are no nodes to return.
-  if (!engine_initialized_) {
+  if (!engine_ || !engine_->IsInitialized()) {
     callback.Run(std::make_unique<base::ListValue>());
     return;
   }
@@ -1747,8 +1745,9 @@
 
 void ProfileSyncService::SetInvalidationsForSessionsEnabled(bool enabled) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-  if (engine_initialized_)
+  if (engine_ && engine_->IsInitialized()) {
     engine_->SetInvalidationsForSessionsEnabled(enabled);
+  }
 }
 
 base::WeakPtr<JsController> ProfileSyncService::GetJsController() {
@@ -1784,7 +1783,7 @@
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   // If we haven't initialized yet, don't configure the DTM as it could cause
   // association to start before a Directory has even been created.
-  if (engine_initialized_) {
+  if (engine_ && engine_->IsInitialized()) {
     DCHECK(engine_);
     // Don't configure datatypes if the setup UI is still on the screen - this
     // is to help multi-screen setting UIs (like iOS) where they don't want to
@@ -1857,10 +1856,9 @@
 
 void ProfileSyncService::FlushDirectory() const {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-  // engine_initialized_ implies engine_ isn't null and the manager exists.
-  // If sync is not initialized yet, we fail silently.
-  if (engine_initialized_)
+  if (engine_ && engine_->IsInitialized()) {
     engine_->FlushDirectory();
+  }
 }
 
 bool ProfileSyncService::IsPassphrasePrompted() const {
@@ -1883,8 +1881,9 @@
 }
 
 void ProfileSyncService::RemoveClientFromServer() const {
-  if (!engine_initialized_)
+  if (!engine_ || !engine_->IsInitialized()) {
     return;
+  }
   const std::string cache_guid = sync_prefs_.GetCacheGuid();
   DCHECK(!cache_guid.empty());
   std::string birthday;
@@ -1961,7 +1960,7 @@
 
   --outstanding_setup_in_progress_handles_;
 
-  if (engine_initialized_) {
+  if (engine_ && engine_->IsInitialized()) {
     // The user closed a setup UI, and will expect their changes to actually
     // take effect now. So we reconfigure here even if another setup UI happens
     // to be open right now.
diff --git a/components/sync/driver/profile_sync_service.h b/components/sync/driver/profile_sync_service.h
index 08ca40e..b978189 100644
--- a/components/sync/driver/profile_sync_service.h
+++ b/components/sync/driver/profile_sync_service.h
@@ -433,9 +433,6 @@
   // is decremented back to zero, Sync setup is marked no longer in progress.
   int outstanding_setup_in_progress_handles_ = 0;
 
-  // Whether the SyncEngine has been initialized.
-  bool engine_initialized_;
-
   // Set when sync receives STOP_SYNC_FOR_DISABLED_ACCOUNT error from server.
   // Prevents ProfileSyncService from starting engine till browser restarted
   // or user signed out.
diff --git a/components/sync/driver/profile_sync_service_startup_unittest.cc b/components/sync/driver/profile_sync_service_startup_unittest.cc
index c03af92..b3f1b85 100644
--- a/components/sync/driver/profile_sync_service_startup_unittest.cc
+++ b/components/sync/driver/profile_sync_service_startup_unittest.cc
@@ -629,6 +629,7 @@
   // Once the engine calls back and says it's initialized, we're just waiting
   // for the user to finish the initial configuration (choosing data types etc.)
   // before actually syncing data.
+  ON_CALL(*sync_engine, IsInitialized()).WillByDefault(Return(true));
   sync_service()->OnEngineInitialized(ModelTypeSet(), WeakHandle<JsBackend>(),
                                       WeakHandle<DataTypeDebugInfoListener>(),
                                       "test-guid", "test-session-name",
@@ -708,6 +709,7 @@
   // Once the engine calls back and says it's initialized, the DataTypeManager
   // will get configured, since initial setup is already done.
   EXPECT_CALL(*data_type_manager, Configure(_, _));
+  ON_CALL(*sync_engine, IsInitialized()).WillByDefault(Return(true));
   sync_service()->OnEngineInitialized(ModelTypeSet(), WeakHandle<JsBackend>(),
                                       WeakHandle<DataTypeDebugInfoListener>(),
                                       "test-guid", "test-session-name",
diff --git a/components/sync/driver/sync_driver_switches.cc b/components/sync/driver/sync_driver_switches.cc
index a9eb7b7..23399d5d 100644
--- a/components/sync/driver/sync_driver_switches.cc
+++ b/components/sync/driver/sync_driver_switches.cc
@@ -92,15 +92,6 @@
 const base::Feature kSyncSupportSecondaryAccount{
     "SyncSupportSecondaryAccount", base::FEATURE_DISABLED_BY_DEFAULT};
 
-// Gates registration and construction of user events machinery. Enabled by
-// default as each use case should have their own gating feature as well.
-const base::Feature kSyncUserEvents{"SyncUserEvents",
-                                    base::FEATURE_ENABLED_BY_DEFAULT};
-
-// Gates emission of FieldTrial events.
-const base::Feature kSyncUserFieldTrialEvents{"SyncUserFieldTrialEvents",
-                                              base::FEATURE_ENABLED_BY_DEFAULT};
-
 // Gates registration for user language detection events.
 const base::Feature kSyncUserLanguageDetectionEvents{
     "SyncUserLanguageDetectionEvents", base::FEATURE_DISABLED_BY_DEFAULT};
diff --git a/components/sync/driver/sync_driver_switches.h b/components/sync/driver/sync_driver_switches.h
index 425e5c2..48d2df3 100644
--- a/components/sync/driver/sync_driver_switches.h
+++ b/components/sync/driver/sync_driver_switches.h
@@ -44,8 +44,6 @@
 extern const base::Feature kSyncPseudoUSSThemes;
 extern const base::Feature kSyncSendTabToSelf;
 extern const base::Feature kSyncSupportSecondaryAccount;
-extern const base::Feature kSyncUserEvents;
-extern const base::Feature kSyncUserFieldTrialEvents;
 extern const base::Feature kSyncUserLanguageDetectionEvents;
 extern const base::Feature kSyncUserTranslationEvents;
 extern const base::Feature kSyncUSSBookmarks;
diff --git a/components/sync/engine/fake_sync_engine.cc b/components/sync/engine/fake_sync_engine.cc
index 0ae2598..9124c2a 100644
--- a/components/sync/engine/fake_sync_engine.cc
+++ b/components/sync/engine/fake_sync_engine.cc
@@ -17,14 +17,20 @@
 
 }  // namespace
 
-FakeSyncEngine::FakeSyncEngine() : fail_initial_download_(false) {}
+FakeSyncEngine::FakeSyncEngine() {}
 FakeSyncEngine::~FakeSyncEngine() {}
 
 void FakeSyncEngine::Initialize(InitParams params) {
-  params.host->OnEngineInitialized(
-      ModelTypeSet(), WeakHandle<JsBackend>(),
-      WeakHandle<DataTypeDebugInfoListener>(), kTestCacheGuid, kTestSessionName,
-      kTestBirthday, /*bag_of_chips=*/"", !fail_initial_download_);
+  bool success = !fail_initial_download_;
+  initialized_ = success;
+  params.host->OnEngineInitialized(ModelTypeSet(), WeakHandle<JsBackend>(),
+                                   WeakHandle<DataTypeDebugInfoListener>(),
+                                   kTestCacheGuid, kTestSessionName,
+                                   kTestBirthday, /*bag_of_chips=*/"", success);
+}
+
+bool FakeSyncEngine::IsInitialized() const {
+  return initialized_;
 }
 
 void FakeSyncEngine::TriggerRefresh(const ModelTypeSet& types) {}
diff --git a/components/sync/engine/fake_sync_engine.h b/components/sync/engine/fake_sync_engine.h
index 74ce1f1..5564a465 100644
--- a/components/sync/engine/fake_sync_engine.h
+++ b/components/sync/engine/fake_sync_engine.h
@@ -29,6 +29,8 @@
   // Immediately calls params.host->OnEngineInitialized.
   void Initialize(InitParams params) override;
 
+  bool IsInitialized() const override;
+
   void TriggerRefresh(const ModelTypeSet& types) override;
 
   void UpdateCredentials(const SyncCredentials& credentials) override;
@@ -93,7 +95,8 @@
   void set_fail_initial_download(bool should_fail);
 
  private:
-  bool fail_initial_download_;
+  bool fail_initial_download_ = false;
+  bool initialized_ = false;
 };
 
 }  // namespace syncer
diff --git a/components/sync/engine/mock_sync_engine.h b/components/sync/engine/mock_sync_engine.h
index db413c0..e8ac6d66 100644
--- a/components/sync/engine/mock_sync_engine.h
+++ b/components/sync/engine/mock_sync_engine.h
@@ -37,6 +37,7 @@
 
   // SyncEngine:
   MOCK_METHOD1(Initialize, void(InitParams));
+  MOCK_CONST_METHOD0(IsInitialized, bool());
   MOCK_METHOD1(TriggerRefresh, void(const ModelTypeSet&));
   MOCK_METHOD1(UpdateCredentials, void(const SyncCredentials&));
   MOCK_METHOD0(InvalidateCredentials, void());
diff --git a/components/sync/engine/sync_engine.h b/components/sync/engine/sync_engine.h
index dbd2153e..b7867a4 100644
--- a/components/sync/engine/sync_engine.h
+++ b/components/sync/engine/sync_engine.h
@@ -101,6 +101,9 @@
   // engine instance. May be null.
   virtual void Initialize(InitParams params) = 0;
 
+  // Returns whether the asynchronous initialization process has finished.
+  virtual bool IsInitialized() const = 0;
+
   // Inform the engine to trigger a sync cycle for |types|.
   virtual void TriggerRefresh(const ModelTypeSet& types) = 0;
 
diff --git a/components/sync/engine_impl/loopback_server/loopback_connection_manager.cc b/components/sync/engine_impl/loopback_server/loopback_connection_manager.cc
index adb88f38..1ed07ed 100644
--- a/components/sync/engine_impl/loopback_server/loopback_connection_manager.cc
+++ b/components/sync/engine_impl/loopback_server/loopback_connection_manager.cc
@@ -19,7 +19,7 @@
 bool LoopbackConnectionManager::PostBufferToPath(
     PostBufferParams* params,
     const std::string& path,
-    const std::string& auth_token) {
+    const std::string& access_token) {
   params->response.http_status_code =
       loopback_server_.HandleCommand(params->buffer_in, &params->buffer_out);
   DCHECK_GE(params->response.http_status_code, 0);
diff --git a/components/sync/engine_impl/loopback_server/loopback_connection_manager.h b/components/sync/engine_impl/loopback_server/loopback_connection_manager.h
index 6c8b7fa..edccb010 100644
--- a/components/sync/engine_impl/loopback_server/loopback_connection_manager.h
+++ b/components/sync/engine_impl/loopback_server/loopback_connection_manager.h
@@ -26,7 +26,7 @@
   // Overridden ServerConnectionManager functions.
   bool PostBufferToPath(PostBufferParams* params,
                         const std::string& path,
-                        const std::string& auth_token) override;
+                        const std::string& access_token) override;
 
   // The loopback server that will handle the requests locally.
   LoopbackServer loopback_server_;
diff --git a/components/sync/engine_impl/net/server_connection_manager.cc b/components/sync/engine_impl/net/server_connection_manager.cc
index 8deaf2f..c056d25 100644
--- a/components/sync/engine_impl/net/server_connection_manager.cc
+++ b/components/sync/engine_impl/net/server_connection_manager.cc
@@ -152,17 +152,17 @@
   return MakeConnection();
 }
 
-bool ServerConnectionManager::SetAuthToken(const std::string& auth_token) {
+bool ServerConnectionManager::SetAccessToken(const std::string& access_token) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
 
-  if (!auth_token.empty()) {
-    auth_token_.assign(auth_token);
+  if (!access_token.empty()) {
+    access_token_.assign(access_token);
     return true;
   }
 
-  auth_token_.clear();
+  access_token_.clear();
 
-  // The auth token could be non-empty in cases like server outage/bug. E.g.
+  // The access token could be non-empty in cases like server outage/bug. E.g.
   // token returned by first request is considered invalid by sync server and
   // because of token server's caching policy, etc, same token is returned on
   // second request. Need to notify sync frontend again to request new token,
@@ -172,8 +172,8 @@
   return false;
 }
 
-void ServerConnectionManager::ClearAuthToken() {
-  auth_token_.clear();
+void ServerConnectionManager::ClearAccessToken() {
+  access_token_.clear();
 }
 
 void ServerConnectionManager::SetServerStatus(
@@ -199,7 +199,7 @@
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   string path =
       MakeSyncServerPath(proto_sync_path(), MakeSyncQueryString(client_id_));
-  bool result = PostBufferToPath(params, path, auth_token());
+  bool result = PostBufferToPath(params, path, access_token_);
   SetServerStatus(params->response.server_status);
   net_error_code_ = params->response.net_error_code;
   return result;
@@ -207,14 +207,14 @@
 
 bool ServerConnectionManager::PostBufferToPath(PostBufferParams* params,
                                                const string& path,
-                                               const string& auth_token) {
+                                               const string& access_token) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
 
-  if (auth_token.empty()) {
+  if (access_token.empty()) {
     params->response.server_status = HttpResponse::SYNC_AUTH_ERROR;
     // Print a log to distinguish this "known failure" from others.
     DVLOG(1) << "ServerConnectionManager forcing SYNC_AUTH_ERROR due to missing"
-                " auth token";
+                " access token";
     return false;
   }
 
@@ -226,11 +226,11 @@
 
   // Note that |post| may be aborted by now, which will just cause Init to fail
   // with CONNECTION_UNAVAILABLE.
-  bool ok = connection->Init(path.c_str(), auth_token, params->buffer_in,
+  bool ok = connection->Init(path.c_str(), access_token, params->buffer_in,
                              &params->response);
 
   if (params->response.server_status == HttpResponse::SYNC_AUTH_ERROR) {
-    auth_token_.clear();
+    access_token_.clear();
   }
 
   if (!ok || net::HTTP_OK != params->response.http_status_code)
diff --git a/components/sync/engine_impl/net/server_connection_manager.h b/components/sync/engine_impl/net/server_connection_manager.h
index 18b4905..b56a72c 100644
--- a/components/sync/engine_impl/net/server_connection_manager.h
+++ b/components/sync/engine_impl/net/server_connection_manager.h
@@ -111,7 +111,7 @@
 
     // Called to initialize and perform an HTTP POST.
     virtual bool Init(const char* path,
-                      const std::string& auth_token,
+                      const std::string& access_token,
                       const std::string& payload,
                       HttpResponse* response) = 0;
 
@@ -149,7 +149,7 @@
   virtual ~ServerConnectionManager();
 
   // POSTS buffer_in and reads a response into buffer_out. Uses our currently
-  // set auth token in our headers.
+  // set access token in our headers.
   //
   // Returns true if executed successfully.
   virtual bool PostBufferWithCachedAuth(PostBufferParams* params);
@@ -179,17 +179,12 @@
     client_id_.assign(client_id);
   }
 
-  // Sets a new auth token. If |auth_token| is empty, the current token is
+  // Sets a new access token. If |access_token| is empty, the current token is
   // invalidated and cleared. Returns false if the server is in authentication
   // error state.
-  bool SetAuthToken(const std::string& auth_token);
+  bool SetAccessToken(const std::string& access_token);
 
-  bool HasInvalidAuthToken() { return auth_token_.empty(); }
-
-  const std::string auth_token() const {
-    DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-    return auth_token_;
-  }
+  bool HasInvalidAccessToken() { return access_token_.empty(); }
 
  protected:
   inline std::string proto_sync_path() const { return proto_sync_path_; }
@@ -202,9 +197,9 @@
   // Internal PostBuffer base function.
   virtual bool PostBufferToPath(PostBufferParams*,
                                 const std::string& path,
-                                const std::string& auth_token);
+                                const std::string& access_token);
 
-  void ClearAuthToken();
+  void ClearAccessToken();
 
   // Helper to check terminated flags and build a Connection object. If this
   // ServerConnectionManager has been terminated, this will return null.
@@ -228,8 +223,8 @@
   // The paths we post to.
   std::string proto_sync_path_;
 
-  // The auth token to use in authenticated requests.
-  std::string auth_token_;
+  // The access token to use in authenticated requests.
+  std::string access_token_;
 
   base::ObserverList<ServerConnectionEventListener>::Unchecked listeners_;
 
diff --git a/components/sync/engine_impl/net/sync_server_connection_manager.cc b/components/sync/engine_impl/net/sync_server_connection_manager.cc
index 07fe6b7..64fceb64 100644
--- a/components/sync/engine_impl/net/sync_server_connection_manager.cc
+++ b/components/sync/engine_impl/net/sync_server_connection_manager.cc
@@ -33,7 +33,7 @@
 }
 
 bool SyncBridgedConnection::Init(const char* path,
-                                 const std::string& auth_token,
+                                 const std::string& access_token,
                                  const std::string& payload,
                                  HttpResponse* response) {
   std::string sync_server;
@@ -45,9 +45,9 @@
   HttpPostProviderInterface* http = post_provider_;
   http->SetURL(connection_url.c_str(), sync_server_port);
 
-  if (!auth_token.empty()) {
+  if (!access_token.empty()) {
     std::string headers;
-    headers = "Authorization: Bearer " + auth_token;
+    headers = "Authorization: Bearer " + access_token;
     http->SetExtraRequestHeaders(headers.c_str());
   }
 
diff --git a/components/sync/engine_impl/net/sync_server_connection_manager.h b/components/sync/engine_impl/net/sync_server_connection_manager.h
index 1848b67..07cbdce 100644
--- a/components/sync/engine_impl/net/sync_server_connection_manager.h
+++ b/components/sync/engine_impl/net/sync_server_connection_manager.h
@@ -30,7 +30,7 @@
   ~SyncBridgedConnection() override;
 
   bool Init(const char* path,
-            const std::string& auth_token,
+            const std::string& access_token,
             const std::string& payload,
             HttpResponse* response) override;
 
diff --git a/components/sync/engine_impl/sync_manager_impl.cc b/components/sync/engine_impl/sync_manager_impl.cc
index 174b8f19..6649ffb 100644
--- a/components/sync/engine_impl/sync_manager_impl.cc
+++ b/components/sync/engine_impl/sync_manager_impl.cc
@@ -464,10 +464,6 @@
   return scheduler_.get();
 }
 
-bool SyncManagerImpl::GetHasInvalidAuthTokenForTest() const {
-  return connection_manager_->HasInvalidAuthToken();
-}
-
 bool SyncManagerImpl::OpenDirectory(const InitArgs* args) {
   DCHECK(!initialized_) << "Should only happen once";
 
@@ -538,7 +534,7 @@
   cycle_context_->set_account_name(credentials.email);
 
   observing_network_connectivity_changes_ = true;
-  if (!connection_manager_->SetAuthToken(credentials.access_token))
+  if (!connection_manager_->SetAccessToken(credentials.access_token))
     return;  // Auth token is known to be invalid, so exit early.
 
   scheduler_->OnCredentialsUpdated();
@@ -548,7 +544,7 @@
 
 void SyncManagerImpl::InvalidateCredentials() {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-  connection_manager_->SetAuthToken(std::string());
+  connection_manager_->SetAccessToken(std::string());
 }
 
 void SyncManagerImpl::AddObserver(SyncManager::Observer* observer) {
diff --git a/components/sync/engine_impl/sync_manager_impl.h b/components/sync/engine_impl/sync_manager_impl.h
index 5462d2c6..10c1a95 100644
--- a/components/sync/engine_impl/sync_manager_impl.h
+++ b/components/sync/engine_impl/sync_manager_impl.h
@@ -173,8 +173,6 @@
 
   const SyncScheduler* scheduler() const;
 
-  bool GetHasInvalidAuthTokenForTest() const;
-
  protected:
   // Helper functions.  Virtual for testing.
   virtual void NotifyInitializationSuccess();
diff --git a/components/sync/engine_impl/sync_scheduler_impl.cc b/components/sync/engine_impl/sync_scheduler_impl.cc
index 856cf4be..ec9b987 100644
--- a/components/sync/engine_impl/sync_scheduler_impl.cc
+++ b/components/sync/engine_impl/sync_scheduler_impl.cc
@@ -155,7 +155,7 @@
   //
   // 1. We're in exponential backoff.
   // 2. We're silenced / throttled.
-  // 3. A nudge was saved previously due to not having a valid auth token.
+  // 3. A nudge was saved previously due to not having a valid access token.
   // 4. A nudge was scheduled + saved while in configuration mode.
   //
   // In all cases except (2), we want to retry contacting the server. We
@@ -288,8 +288,8 @@
   }
 
   if (!ignore_auth_credentials_ &&
-      cycle_context_->connection_manager()->HasInvalidAuthToken()) {
-    SDVLOG(1) << "Unable to run a job because we have no valid auth token.";
+      cycle_context_->connection_manager()->HasInvalidAccessToken()) {
+    SDVLOG(1) << "Unable to run a job because we have no valid access token.";
     return false;
   }
 
@@ -689,7 +689,7 @@
     // We must be in an error state. Transitioning out of each of these
     // error states should trigger a canary job.
     DCHECK(IsGlobalThrottle() || IsGlobalBackoff() ||
-           cycle_context_->connection_manager()->HasInvalidAuthToken());
+           cycle_context_->connection_manager()->HasInvalidAccessToken());
   }
 
   RestartWaiting();
diff --git a/components/sync/engine_impl/sync_scheduler_impl_unittest.cc b/components/sync/engine_impl/sync_scheduler_impl_unittest.cc
index ccb16ce..bf64b2cd 100644
--- a/components/sync/engine_impl/sync_scheduler_impl_unittest.cc
+++ b/components/sync/engine_impl/sync_scheduler_impl_unittest.cc
@@ -514,12 +514,12 @@
   ASSERT_EQ(0, ready_counter.times_called());
 }
 
-// Verify that in the absence of valid auth token the command will fail.
-TEST_F(SyncSchedulerImplTest, ConfigNoAuthToken) {
+// Verify that in the absence of valid access token the command will fail.
+TEST_F(SyncSchedulerImplTest, ConfigNoAccessToken) {
   SyncShareTimes times;
   const ModelTypeSet model_types(THEMES);
 
-  connection()->ResetAuthToken();
+  connection()->ResetAccessToken();
 
   StartSyncConfiguration();
 
@@ -532,14 +532,14 @@
   ASSERT_EQ(0, ready_counter.times_called());
 }
 
-// Verify that in the absence of valid auth token the command will pass if local
-// sync backend is used.
-TEST_F(SyncSchedulerImplTest, ConfigNoAuthTokenLocalSync) {
+// Verify that in the absence of valid access token the command will pass if
+// local sync backend is used.
+TEST_F(SyncSchedulerImplTest, ConfigNoAccessTokenLocalSync) {
   SyncShareTimes times;
   const ModelTypeSet model_types(THEMES);
 
   NewSchedulerForLocalBackend();
-  connection()->ResetAuthToken();
+  connection()->ResetAccessToken();
 
   EXPECT_CALL(*syncer(), ConfigureSyncShare(_, _, _))
       .WillOnce(DoAll(Invoke(test_util::SimulateConfigureSuccess),
diff --git a/components/sync/engine_impl/uss_migrator.cc b/components/sync/engine_impl/uss_migrator.cc
index 1032285..fe8c856 100644
--- a/components/sync/engine_impl/uss_migrator.cc
+++ b/components/sync/engine_impl/uss_migrator.cc
@@ -64,8 +64,6 @@
     DCHECK_NE(BOOKMARKS, type);
   }
 
-  // It looks like there are fancy other ways to get e.g. passwords specifics
-  // out of Entry. Do we need to special-case them when we ship those types?
   entity->mutable_specifics()->CopyFrom(entry.GetServerSpecifics());
   return true;
 }
diff --git a/components/sync/test/engine/mock_connection_manager.cc b/components/sync/test/engine/mock_connection_manager.cc
index 298ac7c..f65915a 100644
--- a/components/sync/test/engine/mock_connection_manager.cc
+++ b/components/sync/test/engine/mock_connection_manager.cc
@@ -27,7 +27,7 @@
 
 namespace syncer {
 
-static char kValidAuthToken[] = "AuthToken";
+static char kValidAccessToken[] = "AccessToken";
 static char kCacheGuid[] = "kqyg7097kro6GSUod+GSg==";
 
 MockConnectionManager::MockConnectionManager(syncable::Directory* directory,
@@ -49,7 +49,7 @@
       next_position_in_parent_(2),
       num_get_updates_requests_(0) {
   SetNewTimestamp(0);
-  SetAuthToken(kValidAuthToken);
+  SetAccessToken(kValidAccessToken);
 }
 
 MockConnectionManager::~MockConnectionManager() {
@@ -72,7 +72,7 @@
 
 bool MockConnectionManager::PostBufferToPath(PostBufferParams* params,
                                              const string& path,
-                                             const string& auth_token) {
+                                             const string& access_token) {
   ClientToServerMessage post;
   if (!post.ParseFromString(params->buffer_in)) {
     ADD_FAILURE();
@@ -108,15 +108,15 @@
     syncable::WriteTransaction wt(FROM_HERE, syncable::UNITTEST, directory_);
   }
 
-  if (auth_token.empty()) {
+  if (access_token.empty()) {
     params->response.server_status = HttpResponse::SYNC_AUTH_ERROR;
     return false;
   }
 
-  if (auth_token != kValidAuthToken) {
+  if (access_token != kValidAccessToken) {
     // Simulate server-side auth failure.
     params->response.server_status = HttpResponse::SYNC_AUTH_ERROR;
-    ClearAuthToken();
+    ClearAccessToken();
   }
 
   if (--countdown_to_postbuffer_fail_ == 0) {
diff --git a/components/sync/test/engine/mock_connection_manager.h b/components/sync/test/engine/mock_connection_manager.h
index 2ac6bbb8..6eb29b6c 100644
--- a/components/sync/test/engine/mock_connection_manager.h
+++ b/components/sync/test/engine/mock_connection_manager.h
@@ -45,7 +45,7 @@
   // Overridden ServerConnectionManager functions.
   bool PostBufferToPath(PostBufferParams*,
                         const std::string& path,
-                        const std::string& auth_token) override;
+                        const std::string& access_token) override;
 
   // Control of commit response.
   // NOTE: Commit callback is invoked only once then reset.
@@ -268,7 +268,7 @@
   // Adds a new progress marker to the last update.
   sync_pb::DataTypeProgressMarker* AddUpdateProgressMarker();
 
-  void ResetAuthToken() { ClearAuthToken(); }
+  void ResetAccessToken() { ClearAccessToken(); }
 
  private:
   sync_pb::SyncEntity* AddUpdateFull(syncable::Id id,
@@ -375,7 +375,7 @@
   // The AUTHENTICATE response we'll return for auth requests.
   sync_pb::AuthenticateResponse auth_response_;
   // What we use to determine if we should return SUCCESS or BAD_AUTH_TOKEN.
-  std::string valid_auth_token_;
+  std::string valid_access_token_;
 
   // Whether we are faking a server mandating clients to throttle requests.
   // Protected by |response_code_override_lock_|.
diff --git a/components/sync/user_events/trial_recorder.cc b/components/sync/user_events/trial_recorder.cc
deleted file mode 100644
index e2580ff8..0000000
--- a/components/sync/user_events/trial_recorder.cc
+++ /dev/null
@@ -1,77 +0,0 @@
-// Copyright 2017 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "components/sync/user_events/trial_recorder.h"
-
-#include <memory>
-#include <set>
-#include <utility>
-
-#include "base/bind.h"
-#include "base/bind_helpers.h"
-#include "base/feature_list.h"
-#include "base/metrics/field_trial.h"
-#include "base/metrics/field_trial_params.h"
-#include "base/stl_util.h"
-#include "base/time/time.h"
-#include "components/sync/driver/sync_driver_switches.h"
-#include "components/variations/active_field_trials.h"
-
-using sync_pb::UserEventSpecifics;
-
-namespace syncer {
-
-namespace {
-
-// A FieldTrial is recorded periodically, using this delay. Although upon chance
-// an event is immediately recorded and we reset to using this delay for the
-// next time.
-base::TimeDelta GetFieldTrialDelay() {
-  return base::TimeDelta::FromSeconds(base::GetFieldTrialParamByFeatureAsInt(
-      switches::kSyncUserFieldTrialEvents, "field_trial_delay_seconds",
-      base::TimeDelta::FromDays(1).InSeconds()));
-}
-
-}  // namespace
-
-TrialRecorder::TrialRecorder(UserEventService* user_event_service)
-    : user_event_service_(user_event_service),
-      variations_(variations::CHROME_SYNC_EVENT_LOGGER,
-                  base::BindRepeating(&TrialRecorder::OnNewVariationId,
-                                      base::Unretained(this))) {
-  DCHECK(user_event_service_);
-  RecordFieldTrials();
-}
-
-TrialRecorder::~TrialRecorder() {}
-
-void TrialRecorder::RecordFieldTrials() {
-  if (!base::FeatureList::IsEnabled(switches::kSyncUserFieldTrialEvents)) {
-    return;
-  }
-
-  std::set<variations::VariationID> ids = variations_.GetIds();
-  if (!ids.empty()) {
-    auto specifics = std::make_unique<UserEventSpecifics>();
-    specifics->set_event_time_usec(
-        (base::Time::Now() - base::Time()).InMicroseconds());
-
-    for (variations::VariationID id : ids) {
-      DCHECK_NE(variations::EMPTY_ID, id);
-      specifics->mutable_field_trial_event()->add_variation_ids(id);
-    }
-    user_event_service_->RecordUserEvent(std::move(specifics));
-  }
-
-  field_trial_timer_.Start(
-      FROM_HERE, GetFieldTrialDelay(),
-      base::BindRepeating(&TrialRecorder::RecordFieldTrials,
-                          base::Unretained(this)));
-}
-
-void TrialRecorder::OnNewVariationId(variations::VariationID id) {
-  RecordFieldTrials();
-}
-
-}  // namespace syncer
diff --git a/components/sync/user_events/trial_recorder.h b/components/sync/user_events/trial_recorder.h
deleted file mode 100644
index 7461a92..0000000
--- a/components/sync/user_events/trial_recorder.h
+++ /dev/null
@@ -1,45 +0,0 @@
-// Copyright 2017 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef COMPONENTS_SYNC_USER_EVENTS_TRIAL_RECORDER_H_
-#define COMPONENTS_SYNC_USER_EVENTS_TRIAL_RECORDER_H_
-
-#include "base/macros.h"
-#include "base/timer/timer.h"
-#include "components/sync/protocol/user_event_specifics.pb.h"
-#include "components/sync/user_events/user_event_service.h"
-#include "components/variations/variations_associated_data.h"
-#include "components/variations/variations_id_collection.h"
-
-namespace syncer {
-
-// Watches finalization of trails and records FieldTrial events through its
-// UserEventService on construction, on change, and every so often.
-class TrialRecorder {
- public:
-  explicit TrialRecorder(UserEventService* user_event_service);
-  ~TrialRecorder();
-
- private:
-  // Construct and record a field trial event if applicable.
-  void RecordFieldTrials();
-
-  // Simply drops the |id| param and calls RecordFieldTrials().
-  void OnNewVariationId(variations::VariationID id);
-
-  // Non-owning pointer to interface of how events are actually recorded.
-  UserEventService* user_event_service_;
-
-  // Tracks all the variation ids that we we care about.
-  variations::VariationsIdCollection variations_;
-
-  // Timer used to record a field trial event every given interval.
-  base::OneShotTimer field_trial_timer_;
-
-  DISALLOW_COPY_AND_ASSIGN(TrialRecorder);
-};
-
-}  // namespace syncer
-
-#endif  // COMPONENTS_SYNC_USER_EVENTS_TRIAL_RECORDER_H_
diff --git a/components/sync/user_events/trial_recorder_unittest.cc b/components/sync/user_events/trial_recorder_unittest.cc
deleted file mode 100644
index c8735ec..0000000
--- a/components/sync/user_events/trial_recorder_unittest.cc
+++ /dev/null
@@ -1,162 +0,0 @@
-// Copyright 2017 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "components/sync/user_events/trial_recorder.h"
-
-#include <memory>
-#include <set>
-#include <string>
-
-#include "base/metrics/field_trial.h"
-#include "base/run_loop.h"
-#include "base/test/scoped_feature_list.h"
-#include "base/test/scoped_task_environment.h"
-#include "components/sync/driver/sync_driver_switches.h"
-#include "components/sync/protocol/sync.pb.h"
-#include "components/sync/user_events/fake_user_event_service.h"
-#include "components/variations/variations_associated_data.h"
-#include "testing/gtest/include/gtest/gtest.h"
-
-using sync_pb::UserEventSpecifics;
-
-namespace syncer {
-
-namespace {
-
-const char kTrial1[] = "TrialNameOne";
-const char kTrial2[] = "TrialNameTwo";
-const char kGroup[] = "GroupName";
-const variations::VariationID kVariation1 = 111;
-const variations::VariationID kVariation2 = 222;
-
-void VerifyEvent(std::set<variations::VariationID> expected_variations,
-                 const UserEventSpecifics& actual) {
-  ASSERT_EQ(
-      expected_variations.size(),
-      static_cast<size_t>(actual.field_trial_event().variation_ids_size()));
-  for (int actaul_variation : actual.field_trial_event().variation_ids()) {
-    auto iter = expected_variations.find(actaul_variation);
-    if (iter == expected_variations.end()) {
-      FAIL() << actaul_variation;
-    } else {
-      // Remove to make sure the event doesn't contain duplicates.
-      expected_variations.erase(iter);
-    }
-  }
-}
-
-void SetupAndFinalizeTrial(const std::string& trial_name,
-                           variations::VariationID id) {
-  variations::AssociateGoogleVariationID(variations::CHROME_SYNC_EVENT_LOGGER,
-                                         trial_name, kGroup, id);
-  base::FieldTrialList::CreateFieldTrial(trial_name, kGroup);
-  base::FieldTrialList::FindFullName(trial_name);
-  base::RunLoop().RunUntilIdle();
-}
-
-class TrialRecorderTest : public testing::Test {
- public:
-  TrialRecorderTest() : field_trial_list_(nullptr) {}
-
-  ~TrialRecorderTest() override { variations::testing::ClearAllVariationIDs(); }
-
-  FakeUserEventService* service() { return &service_; }
-  TrialRecorder* recorder() { return recorder_.get(); }
-
-  void VerifyLastEvent(std::set<variations::VariationID> expected_variations) {
-    ASSERT_LE(1u, service()->GetRecordedUserEvents().size());
-    VerifyEvent(expected_variations,
-                *service()->GetRecordedUserEvents().rbegin());
-  }
-
-  void InitRecorder() {
-    recorder_ = std::make_unique<TrialRecorder>(&service_);
-  }
-
- private:
-  base::test::ScopedTaskEnvironment task_environment_;
-  base::FieldTrialList field_trial_list_;
-  FakeUserEventService service_;
-  std::unique_ptr<TrialRecorder> recorder_;
-};
-
-TEST_F(TrialRecorderTest, FinalizedBeforeInit) {
-  SetupAndFinalizeTrial(kTrial1, kVariation1);
-  SetupAndFinalizeTrial(kTrial2, kVariation2);
-  // Should check on initialization to see if there are any trails to record.
-  InitRecorder();
-  VerifyLastEvent({kVariation1, kVariation2});
-}
-
-TEST_F(TrialRecorderTest, FinalizedAfterInit) {
-  InitRecorder();
-  SetupAndFinalizeTrial(kTrial1, kVariation1);
-  SetupAndFinalizeTrial(kTrial2, kVariation2);
-  VerifyLastEvent({kVariation1, kVariation2});
-}
-
-TEST_F(TrialRecorderTest, FinalizedMix) {
-  SetupAndFinalizeTrial(kTrial1, kVariation1);
-  InitRecorder();
-  VerifyLastEvent({kVariation1});
-
-  SetupAndFinalizeTrial(kTrial2, kVariation2);
-  VerifyLastEvent({kVariation1, kVariation2});
-}
-
-TEST_F(TrialRecorderTest, WrongVariationKey) {
-  InitRecorder();
-  variations::AssociateGoogleVariationID(variations::GOOGLE_WEB_PROPERTIES,
-                                         kTrial1, kGroup, kVariation1);
-  base::FieldTrialList::CreateFieldTrial(kTrial1, kGroup);
-  base::FieldTrialList::FindFullName(kTrial1);
-  base::RunLoop().RunUntilIdle();
-
-  EXPECT_EQ(0u, service()->GetRecordedUserEvents().size());
-}
-
-TEST_F(TrialRecorderTest, NoVariation) {
-  InitRecorder();
-  base::FieldTrialList::CreateFieldTrial(kTrial1, kGroup);
-  base::FieldTrialList::FindFullName(kTrial1);
-  base::RunLoop().RunUntilIdle();
-
-  EXPECT_EQ(0u, service()->GetRecordedUserEvents().size());
-}
-
-TEST_F(TrialRecorderTest, FieldTrialFeatureDisabled) {
-  base::test::ScopedFeatureList scoped_feature_list;
-  scoped_feature_list.InitAndDisableFeature(
-      switches::kSyncUserFieldTrialEvents);
-  InitRecorder();
-  SetupAndFinalizeTrial(kTrial1, kVariation1);
-
-  EXPECT_EQ(0u, service()->GetRecordedUserEvents().size());
-}
-
-TEST_F(TrialRecorderTest, FieldTrialTimer) {
-  SetupAndFinalizeTrial(kTrial2, kVariation2);
-
-  {
-    // Start with 0 delay, which should mean we post immediately to record.
-    // Need to be not call any methods that might invoke RunUntilIdle() while we
-    // have a delay of 0, like SetupAndFinalizeTrial().
-    base::test::ScopedFeatureList scoped_feature_list;
-    scoped_feature_list.InitAndEnableFeatureWithParameters(
-        switches::kSyncUserFieldTrialEvents,
-        {{"field_trial_delay_seconds", "0"}});
-    InitRecorder();
-    EXPECT_EQ(1u, service()->GetRecordedUserEvents().size());
-  }
-
-  // Now that |scoped_feature_list| is gone, we should reset to default,
-  // otherwise our RunUntilIdle() would infinitively loop with a delay of 0.
-  base::RunLoop().RunUntilIdle();
-  // Should have picked up exactly one more event.
-  EXPECT_EQ(2u, service()->GetRecordedUserEvents().size());
-}
-
-}  // namespace
-
-}  // namespace syncer
diff --git a/components/sync/user_events/user_event_service_impl.cc b/components/sync/user_events/user_event_service_impl.cc
index be026b2..db28eeb 100644
--- a/components/sync/user_events/user_event_service_impl.cc
+++ b/components/sync/user_events/user_event_service_impl.cc
@@ -6,7 +6,6 @@
 
 #include <utility>
 
-#include "base/feature_list.h"
 #include "base/rand_util.h"
 #include "base/stl_util.h"
 #include "base/time/time.h"
@@ -66,8 +65,7 @@
     std::unique_ptr<UserEventSyncBridge> bridge)
     : sync_service_(sync_service),
       bridge_(std::move(bridge)),
-      session_id_(base::RandUint64()),
-      trial_recorder_(this) {
+      session_id_(base::RandUint64()) {
   DCHECK(bridge_);
   DCHECK(sync_service_);
 }
@@ -97,8 +95,7 @@
 // static
 bool UserEventServiceImpl::MightRecordEvents(bool off_the_record,
                                              SyncService* sync_service) {
-  return !off_the_record && sync_service &&
-         base::FeatureList::IsEnabled(switches::kSyncUserEvents);
+  return !off_the_record && sync_service;
 }
 
 bool UserEventServiceImpl::CanRecordHistory() {
diff --git a/components/sync/user_events/user_event_service_impl.h b/components/sync/user_events/user_event_service_impl.h
index a1aa9e1..8c421c846 100644
--- a/components/sync/user_events/user_event_service_impl.h
+++ b/components/sync/user_events/user_event_service_impl.h
@@ -12,7 +12,6 @@
 #include "base/memory/weak_ptr.h"
 #include "components/keyed_service/core/keyed_service.h"
 #include "components/sync/protocol/user_event_specifics.pb.h"
-#include "components/sync/user_events/trial_recorder.h"
 #include "components/sync/user_events/user_event_service.h"
 
 namespace syncer {
@@ -57,9 +56,6 @@
   // which events came from the same session.
   uint64_t session_id_;
 
-  // Tracks and records field trails when appropriate.
-  TrialRecorder trial_recorder_;
-
   DISALLOW_COPY_AND_ASSIGN(UserEventServiceImpl);
 };
 
diff --git a/components/sync/user_events/user_event_service_impl_unittest.cc b/components/sync/user_events/user_event_service_impl_unittest.cc
index b78f650a99..7b86abd 100644
--- a/components/sync/user_events/user_event_service_impl_unittest.cc
+++ b/components/sync/user_events/user_event_service_impl_unittest.cc
@@ -7,8 +7,6 @@
 #include <utility>
 #include <vector>
 
-#include "base/metrics/field_trial.h"
-#include "base/test/scoped_feature_list.h"
 #include "base/test/scoped_task_environment.h"
 #include "components/sync/base/model_type.h"
 #include "components/sync/driver/sync_driver_switches.h"
@@ -20,7 +18,6 @@
 #include "components/variations/variations_associated_data.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
-using base::test::ScopedFeatureList;
 using sync_pb::UserEventSpecifics;
 using testing::_;
 
@@ -57,13 +54,6 @@
   return specifics;
 }
 
-MATCHER_P(HasFieldTrialVariationIds, expected_variation_id, "") {
-  const UserEventSpecifics& specifics = arg->specifics.user_event();
-  return specifics.field_trial_event().variation_ids_size() == 1 &&
-         specifics.field_trial_event().variation_ids(0) ==
-             expected_variation_id;
-}
-
 class TestGlobalIdMapper : public GlobalIdMapper {
   void AddGlobalIdChangeObserver(GlobalIdChange callback) override {}
   int64_t GetLatestGlobalId(int64_t global_id) override { return global_id; }
@@ -71,7 +61,7 @@
 
 class UserEventServiceImplTest : public testing::Test {
  protected:
-  UserEventServiceImplTest() : field_trial_list_(nullptr) {
+  UserEventServiceImplTest() {
     sync_service_.SetPreferredDataTypes(
         {HISTORY_DELETE_DIRECTIVES, USER_EVENTS});
     ON_CALL(mock_processor_, IsTrackingMetadata())
@@ -91,15 +81,12 @@
 
  private:
   base::test::ScopedTaskEnvironment task_environment_;
-  base::FieldTrialList field_trial_list_;
   syncer::TestSyncService sync_service_;
   testing::NiceMock<MockModelTypeChangeProcessor> mock_processor_;
   TestGlobalIdMapper mapper_;
-
-  base::test::ScopedFeatureList feature_list_;
 };
 
-TEST_F(UserEventServiceImplTest, MightRecordEventsFeatureEnabled) {
+TEST_F(UserEventServiceImplTest, MightRecordEvents) {
   // All conditions are met, might record.
   EXPECT_TRUE(UserEventServiceImpl::MightRecordEvents(false, sync_service()));
   // No sync service, will not record.
@@ -108,13 +95,6 @@
   EXPECT_FALSE(UserEventServiceImpl::MightRecordEvents(true, sync_service()));
 }
 
-TEST_F(UserEventServiceImplTest, MightRecordEventsFeatureDisabled) {
-  // Will not record because the default on feature is overridden.
-  ScopedFeatureList scoped_feature_list;
-  scoped_feature_list.InitAndDisableFeature(switches::kSyncUserEvents);
-  EXPECT_FALSE(UserEventServiceImpl::MightRecordEvents(false, sync_service()));
-}
-
 TEST_F(UserEventServiceImplTest, ShouldRecord) {
   UserEventServiceImpl service(sync_service(), MakeBridge());
   EXPECT_CALL(*mock_processor(), Put(_, _, _));
@@ -205,16 +185,6 @@
   EXPECT_NE(put_session_ids[0], put_session_ids[1]);
 }
 
-TEST_F(UserEventServiceImplTest, FieldTrial) {
-  variations::AssociateGoogleVariationID(variations::CHROME_SYNC_EVENT_LOGGER,
-                                         "trial", "group", 123);
-  base::FieldTrialList::CreateFieldTrial("trial", "group");
-  base::FieldTrialList::FindFullName("trial");
-
-  EXPECT_CALL(*mock_processor(), Put(_, HasFieldTrialVariationIds(123u), _));
-  UserEventServiceImpl service(sync_service(), MakeBridge());
-}
-
 TEST_F(UserEventServiceImplTest, ShouldNotRecordWhenEventsDatatypeIsDisabled) {
   sync_service()->SetPreferredDataTypes({HISTORY_DELETE_DIRECTIVES});
   UserEventServiceImpl service(sync_service(), MakeBridge());
diff --git a/components/translate/translate_internals/translate_internals.html b/components/translate/translate_internals/translate_internals.html
index e4d4ceab..061cbcd 100644
--- a/components/translate/translate_internals/translate_internals.html
+++ b/components/translate/translate_internals/translate_internals.html
@@ -11,6 +11,10 @@
     <link rel="stylesheet" href="chrome://resources/css/text_defaults.css">
     <link rel="stylesheet" href="chrome://resources/css/tabs.css">
     <link rel="stylesheet" href="./translate_internals.css">
+<if expr="is_ios">
+  <!-- TODO(crbug.com/487000): Remove this once injected by web. -->
+  <script src="chrome://resources/js/ios/web_ui.js"></script>
+</if>
     <script src="chrome://resources/js/util.js"></script>
     <script src="chrome://resources/js/cr.js"></script>
     <script src="chrome://resources/js/cr/event_target.js"></script>
diff --git a/components/update_client/protocol_parser_json.cc b/components/update_client/protocol_parser_json.cc
index 09dd08f..eb022e43 100644
--- a/components/update_client/protocol_parser_json.cc
+++ b/components/update_client/protocol_parser_json.cc
@@ -299,12 +299,13 @@
     return false;
   }
   const auto* protocol = response_node->FindKey("protocol");
-  if (!protocol || !protocol->is_string() ||
-      protocol->GetString() != kProtocolVersion) {
-    ParseError(
-        "Missing/incorrect protocol."
-        "(expected '%s', found '%s')",
-        kProtocolVersion, protocol->GetString().c_str());
+  if (!protocol || !protocol->is_string()) {
+    ParseError("Missing/non-string protocol.");
+    return false;
+  }
+  if (protocol->GetString() != kProtocolVersion) {
+    ParseError("Incorrect protocol. (expected '%s', found '%s')",
+               kProtocolVersion, protocol->GetString().c_str());
     return false;
   }
 
diff --git a/components/variations/service/variations_service.cc b/components/variations/service/variations_service.cc
index 7eb2180f..c2807a85 100644
--- a/components/variations/service/variations_service.cc
+++ b/components/variations/service/variations_service.cc
@@ -924,6 +924,11 @@
   pending_seed_request_.reset();
 }
 
+void VariationsService::StartRepeatedVariationsSeedFetchForTesting() {
+  InitResourceRequestedAllowedNotifier();
+  return StartRepeatedVariationsSeedFetch();
+}
+
 std::string VariationsService::GetStoredPermanentCountry() {
   const base::ListValue* list_value =
       local_state_->GetList(prefs::kVariationsPermanentConsistencyCountry);
diff --git a/components/variations/service/variations_service.h b/components/variations/service/variations_service.h
index bc5fd826b..ebe1a83 100644
--- a/components/variations/service/variations_service.h
+++ b/components/variations/service/variations_service.h
@@ -198,6 +198,9 @@
   // Cancels the currently pending fetch request.
   void CancelCurrentRequestForTesting();
 
+  // Exposes StartRepeatedVariationsSeedFetch for testing.
+  void StartRepeatedVariationsSeedFetchForTesting();
+
  protected:
   // Starts the fetching process once, where |OnURLFetchComplete| is called with
   // the response. This calls DoFetchToURL with the set url.
diff --git a/components/variations/service/variations_service_unittest.cc b/components/variations/service/variations_service_unittest.cc
index 0eb1a64..b7cc43fa 100644
--- a/components/variations/service/variations_service_unittest.cc
+++ b/components/variations/service/variations_service_unittest.cc
@@ -1040,6 +1040,38 @@
                                       net::ERR_FAILED, 1);
 }
 
+TEST_F(VariationsServiceTest,
+       VariationsServiceStartsRequestOnNetworkChange) {
+  // Verifies VariationsService does a request when network status changes from
+  // none to connected. This is a regression test for https://crbug.com/826930.
+  VariationsService::EnableFetchForTesting();
+  network_tracker_->SetConnectionType(
+      network::mojom::ConnectionType::CONNECTION_NONE);
+  TestVariationsService service(
+      std::make_unique<web_resource::TestRequestAllowedNotifier>(
+          &prefs_, network_tracker_),
+      &prefs_, GetMetricsStateManager(), true);
+  service.set_intercepts_fetch(false);
+  service.CancelCurrentRequestForTesting();
+  base::RunLoop().RunUntilIdle();
+  // Simulate starting Chrome browser.
+  service.StartRepeatedVariationsSeedFetchForTesting();
+  const int initial_request_count = service.request_count();
+  // The variations seed can not be fetched if disconnected. So even we start
+  // repeated variations seed fetch (on Chrome start), no requests will be made.
+  EXPECT_EQ(0, initial_request_count);
+
+  service.GetResourceRequestAllowedNotifierForTesting()
+      ->SetObserverRequestedForTesting(true);
+  network_tracker_->SetConnectionType(
+      network::mojom::ConnectionType::CONNECTION_WIFI);
+  base::RunLoop().RunUntilIdle();
+
+  const int final_request_count = service.request_count();
+  // The request will be made once Chrome gets online.
+  EXPECT_EQ(initial_request_count + 1, final_request_count);
+}
+
 // TODO(isherman): Add an integration test for saving and loading a safe seed,
 // once the loading functionality is implemented on the seed store.
 
diff --git a/components/viz/common/quads/frame_deadline.cc b/components/viz/common/quads/frame_deadline.cc
index a332727..4af2eb08 100644
--- a/components/viz/common/quads/frame_deadline.cc
+++ b/components/viz/common/quads/frame_deadline.cc
@@ -27,6 +27,10 @@
   return frame_start_time_ + deadline_in_frames * frame_interval_;
 }
 
+bool FrameDeadline::IsZero() const {
+  return deadline_in_frames_ == 0 && !use_default_lower_bound_deadline_;
+}
+
 std::string FrameDeadline::ToString() const {
   const base::TimeDelta start_time_delta =
       frame_start_time_ - base::TimeTicks();
diff --git a/components/viz/common/quads/frame_deadline.h b/components/viz/common/quads/frame_deadline.h
index be7a80a..e980980 100644
--- a/components/viz/common/quads/frame_deadline.h
+++ b/components/viz/common/quads/frame_deadline.h
@@ -68,6 +68,8 @@
     return use_default_lower_bound_deadline_;
   }
 
+  bool IsZero() const;
+
   std::string ToString() const;
 
  private:
diff --git a/components/viz/service/frame_sinks/video_capture/frame_sink_video_capturer_impl.cc b/components/viz/service/frame_sinks/video_capture/frame_sink_video_capturer_impl.cc
index cd0e3c1..89cbf1a 100644
--- a/components/viz/service/frame_sinks/video_capture/frame_sink_video_capturer_impl.cc
+++ b/components/viz/service/frame_sinks/video_capture/frame_sink_video_capturer_impl.cc
@@ -141,6 +141,7 @@
   }
 
   if (format_changed) {
+    frame_pool_.ClearFrameMarking();
     RefreshEntireSourceSoon();
   }
 }
@@ -297,7 +298,7 @@
 void FrameSinkVideoCapturerImpl::RefreshEntireSourceSoon() {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
 
-  dirty_rect_ = kMaxRect;
+  InvalidateEntireSource();
   RefreshSoon();
 }
 
@@ -327,11 +328,11 @@
   }
   if (source_size != oracle_->source_size()) {
     oracle_->SetSourceSize(source_size);
-    dirty_rect_ = kMaxRect;
+    InvalidateEntireSource();
   }
 
-  MaybeCaptureFrame(VideoCaptureOracle::kRefreshRequest,
-                    gfx::Rect(oracle_->source_size()), clock_->NowTicks(),
+  MaybeCaptureFrame(VideoCaptureOracle::kRefreshRequest, gfx::Rect(),
+                    clock_->NowTicks(),
                     *resolved_target_->GetLastActivatedFrameMetadata());
 }
 
@@ -350,7 +351,7 @@
     InvalidateRect(damage_rect);
   } else {
     oracle_->SetSourceSize(frame_size);
-    dirty_rect_ = kMaxRect;
+    InvalidateEntireSource();
   }
 
   MaybeCaptureFrame(VideoCaptureOracle::kCompositorUpdate, damage_rect,
@@ -369,6 +370,14 @@
   gfx::Rect positive_rect = rect;
   positive_rect.Intersect(kMaxRect);
   dirty_rect_.Union(positive_rect);
+  content_version_++;
+}
+
+void FrameSinkVideoCapturerImpl::InvalidateEntireSource() {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+
+  dirty_rect_ = kMaxRect;
+  content_version_++;
 }
 
 void FrameSinkVideoCapturerImpl::OnOverlayConnectionLost(
@@ -431,27 +440,19 @@
     return;
   }
 
-  // Reserve a buffer from the pool for the next frame. Optimization: If there
-  // are no changes in the source that need to be captured AND there are no
-  // captures currently in-flight, attempt to resurrect the last frame from the
-  // pool (and there is no need to capture anything new into the frame).
+  // Reserve a buffer from the pool for the next frame.
   const OracleFrameNumber oracle_frame_number = oracle_->next_frame_number();
   const gfx::Size capture_size =
       AdjustSizeForPixelFormat(oracle_->capture_size());
 
+  const bool can_resurrect_content =
+      content_version_in_marked_frame_ == content_version_ &&
+      frame_pool_.HasMarkedFrameWithSize(capture_size);
   scoped_refptr<VideoFrame> frame;
-  bool using_resurrected_frame =
-      dirty_rect_.IsEmpty() &&
-      next_capture_frame_number_ == next_delivery_frame_number_;
-  if (using_resurrected_frame) {
-    frame = frame_pool_.ResurrectLastVideoFrame(pixel_format_, capture_size);
-    // If the resurrection failed, promote to a full frame capture.
-    if (!frame) {
-      TRACE_EVENT_INSTANT0("gpu.capture", "ResurrectionFailed",
-                           TRACE_EVENT_SCOPE_THREAD);
-      using_resurrected_frame = false;
-      frame = frame_pool_.ReserveVideoFrame(pixel_format_, capture_size);
-    }
+  if (can_resurrect_content) {
+    TRACE_EVENT_INSTANT0("gpu.capture", "UsingResurrectedFrame",
+                         TRACE_EVENT_SCOPE_THREAD);
+    frame = frame_pool_.ResurrectOrDuplicateContentFromMarkedFrame();
   } else {
     frame = frame_pool_.ReserveVideoFrame(pixel_format_, capture_size);
   }
@@ -564,16 +565,16 @@
       frame->set_color_space(gfx::ColorSpace::CreateSRGB());
     }
     dirty_rect_ = gfx::Rect();
-    DidCaptureFrame(capture_frame_number, oracle_frame_number, gfx::Rect(),
-                    std::move(frame));
+    OnFrameReadyForDelivery(capture_frame_number, oracle_frame_number,
+                            gfx::Rect(), std::move(frame));
     return;
   }
 
   // If the frame is a resurrected one, just deliver it since it already
   // contains the most up-to-date capture of the source content.
-  if (using_resurrected_frame) {
-    DidCaptureFrame(capture_frame_number, oracle_frame_number, content_rect,
-                    std::move(frame));
+  if (can_resurrect_content) {
+    OnFrameReadyForDelivery(capture_frame_number, oracle_frame_number,
+                            content_rect, std::move(frame));
     return;
   }
 
@@ -584,7 +585,7 @@
           : CopyOutputRequest::ResultFormat::RGBA_BITMAP,
       base::BindOnce(&FrameSinkVideoCapturerImpl::DidCopyFrame,
                      capture_weak_factory_.GetWeakPtr(), capture_frame_number,
-                     oracle_frame_number, content_rect,
+                     oracle_frame_number, content_version_, content_rect,
                      VideoCaptureOverlay::MakeCombinedRenderer(
                          GetOverlaysInOrder(), content_rect, frame->format()),
                      std::move(frame))));
@@ -599,16 +600,7 @@
   request->set_result_selection(gfx::Rect(content_rect.size()));
 
   // Clear the |dirty_rect_|, to indicate all changes at the source are now
-  // being captured. This will also enable the "frame resurrection" optimization
-  // in future calls to this method. In other words, while the source content
-  // remains unchanged, there is no need to make any more CopyOutputRequests.
-  //
-  // Note that some optimistic assumptions are being made here: 1) that this
-  // |request| will succeed and it's image data successfully transferred to the
-  // VideoFrame; and 2) that delivery of the VideoFrame to the consumer will
-  // succeed. If, later in the pipeline, either of these assumptions is
-  // violated, the |dirty_rect_| will be changed to indicate that there might
-  // still be source changes requiring capture. See MaybeDeliverFrame().
+  // being captured.
   dirty_rect_ = gfx::Rect();
 
   resolved_target_->RequestCopyOfOutput(LocalSurfaceId(), std::move(request));
@@ -617,6 +609,7 @@
 void FrameSinkVideoCapturerImpl::DidCopyFrame(
     int64_t capture_frame_number,
     OracleFrameNumber oracle_frame_number,
+    int64_t content_version,
     const gfx::Rect& content_rect,
     VideoCaptureOverlay::OnceRenderer overlay_renderer,
     scoped_refptr<VideoFrame> frame,
@@ -680,13 +673,18 @@
     media::LetterboxVideoFrame(
         frame.get(), gfx::Rect(content_rect.origin(),
                                AdjustSizeForPixelFormat(result->size())));
+
+    if (content_version > content_version_in_marked_frame_) {
+      frame_pool_.MarkFrame(*frame);
+      content_version_in_marked_frame_ = content_version;
+    }
   }
 
-  DidCaptureFrame(capture_frame_number, oracle_frame_number, content_rect,
-                  std::move(frame));
+  OnFrameReadyForDelivery(capture_frame_number, oracle_frame_number,
+                          content_rect, std::move(frame));
 }
 
-void FrameSinkVideoCapturerImpl::DidCaptureFrame(
+void FrameSinkVideoCapturerImpl::OnFrameReadyForDelivery(
     int64_t capture_frame_number,
     OracleFrameNumber oracle_frame_number,
     const gfx::Rect& content_rect,
@@ -733,12 +731,6 @@
     TRACE_EVENT_ASYNC_END1("gpu.capture", "Capture", oracle_frame_number,
                            "success", false);
 
-    // Mark the whole source as dirty, since this frame may have contained
-    // updated content that will not be delivered. See the comment at the end of
-    // MaybeCaptureFrame() regarding "optimistic assumptions" for further
-    // discussion.
-    dirty_rect_ = kMaxRect;
-
     ScheduleRefreshFrame();
     return;
   }
diff --git a/components/viz/service/frame_sinks/video_capture/frame_sink_video_capturer_impl.h b/components/viz/service/frame_sinks/video_capture/frame_sink_video_capturer_impl.h
index 3c5ddc07..b35bf1e 100644
--- a/components/viz/service/frame_sinks/video_capture/frame_sink_video_capturer_impl.h
+++ b/components/viz/service/frame_sinks/video_capture/frame_sink_video_capturer_impl.h
@@ -174,6 +174,8 @@
   void InvalidateRect(const gfx::Rect& rect) final;
   void OnOverlayConnectionLost(VideoCaptureOverlay* overlay) final;
 
+  void InvalidateEntireSource();
+
   // Returns a list of the overlays in rendering order.
   std::vector<VideoCaptureOverlay*> GetOverlaysInOrder() const;
 
@@ -189,6 +191,7 @@
   // |content_rect| region of a [possibly letterboxed] video |frame|.
   void DidCopyFrame(int64_t capture_frame_number,
                     OracleFrameNumber oracle_frame_number,
+                    int64_t content_version,
                     const gfx::Rect& content_rect,
                     VideoCaptureOverlay::OnceRenderer overlay_renderer,
                     scoped_refptr<media::VideoFrame> frame,
@@ -197,10 +200,10 @@
   // Places the frame in the |delivery_queue_| and calls MaybeDeliverFrame(),
   // one frame at a time, in-order. |frame| may be null to indicate a
   // completed, but unsuccessful capture.
-  void DidCaptureFrame(int64_t capture_frame_number,
-                       OracleFrameNumber oracle_frame_number,
-                       const gfx::Rect& content_rect,
-                       scoped_refptr<media::VideoFrame> frame);
+  void OnFrameReadyForDelivery(int64_t capture_frame_number,
+                               OracleFrameNumber oracle_frame_number,
+                               const gfx::Rect& content_rect,
+                               scoped_refptr<media::VideoFrame> frame);
 
   // Delivers a |frame| to the consumer, if the VideoCaptureOracle allows
   // it. |frame| can be null to indicate a completed, but unsuccessful capture.
@@ -281,6 +284,12 @@
   // frames in-flight at any one time.
   InterprocessFramePool frame_pool_;
 
+  // Increased every time the source content changes or a forced refresh is
+  // requested.
+  int64_t content_version_ = 0;
+
+  int64_t content_version_in_marked_frame_ = -1;
+
   // A queue of captured frames pending delivery. This queue is used to re-order
   // frames, if they should happen to be captured out-of-order.
   struct CapturedFrame {
diff --git a/components/viz/service/frame_sinks/video_capture/interprocess_frame_pool.cc b/components/viz/service/frame_sinks/video_capture/interprocess_frame_pool.cc
index d13824dd..2ca164c 100644
--- a/components/viz/service/frame_sinks/video_capture/interprocess_frame_pool.cc
+++ b/components/viz/service/frame_sinks/video_capture/interprocess_frame_pool.cc
@@ -30,10 +30,6 @@
     const gfx::Size& size) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
 
-  // Calling this method is a signal that there is no intention of resurrecting
-  // the last frame.
-  resurrectable_buffer_memory_ = nullptr;
-
   const size_t bytes_required = VideoFrame::AllocationSize(format, size);
 
   // Look for an available buffer that's large enough. If one is found, wrap it
@@ -56,6 +52,8 @@
                          [](const PooledBuffer& a, const PooledBuffer& b) {
                            return a.mapping.size() < b.mapping.size();
                          });
+    if (it->mapping.memory() == marked_frame_buffer_)
+      marked_frame_buffer_ = nullptr;
     available_buffers_.erase(it.base() - 1);  // Release before allocating more.
     PooledBuffer reallocated =
         mojo::CreateReadOnlySharedMemoryRegion(bytes_required);
@@ -82,33 +80,78 @@
   return WrapBuffer(std::move(additional), format, size);
 }
 
-scoped_refptr<VideoFrame> InterprocessFramePool::ResurrectLastVideoFrame(
-    VideoPixelFormat expected_format,
-    const gfx::Size& expected_size) {
+void InterprocessFramePool::MarkFrame(const media::VideoFrame& frame) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  marked_frame_buffer_ = frame.data(0);
+  marked_frame_size_ = frame.coded_size();
+  marked_frame_color_space_ = frame.ColorSpace();
+  marked_frame_pixel_format_ = frame.format();
+}
+
+void InterprocessFramePool::ClearFrameMarking() {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  marked_frame_buffer_ = nullptr;
+}
+
+bool InterprocessFramePool::HasMarkedFrameWithSize(
+    const gfx::Size& size) const {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  return marked_frame_buffer_ != nullptr && marked_frame_size_ == size;
+}
+
+scoped_refptr<VideoFrame>
+InterprocessFramePool::ResurrectOrDuplicateContentFromMarkedFrame() {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
 
-  // Find the tracking entry for the resurrectable buffer. If it is still being
-  // used, or is not of the expected format and size, punt.
-  if (resurrectable_buffer_memory_ == nullptr ||
-      last_delivered_format_ != expected_format ||
-      last_delivered_size_ != expected_size) {
+  if (!marked_frame_buffer_)
     return nullptr;
-  }
-  const auto it = std::find_if(
-      available_buffers_.rbegin(), available_buffers_.rend(),
-      [this](const PooledBuffer& candidate) {
-        return candidate.mapping.memory() == resurrectable_buffer_memory_;
-      });
-  if (it == available_buffers_.rend()) {
-    return nullptr;
+
+  const auto it =
+      std::find_if(available_buffers_.rbegin(), available_buffers_.rend(),
+                   [this](const PooledBuffer& candidate) {
+                     return candidate.mapping.memory() == marked_frame_buffer_;
+                   });
+
+  // If the buffer is available, use it directly.
+  if (it != available_buffers_.rend()) {
+    // Wrap the buffer in a VideoFrame and return it.
+    PooledBuffer resurrected = std::move(*it);
+    available_buffers_.erase(it.base() - 1);
+    auto frame = WrapBuffer(std::move(resurrected), marked_frame_pixel_format_,
+                            marked_frame_size_);
+    frame->set_color_space(marked_frame_color_space_);
+    return frame;
   }
 
-  // Wrap the buffer in a VideoFrame and return it.
-  PooledBuffer resurrected = std::move(*it);
-  available_buffers_.erase(it.base() - 1);
+  // Buffer is currently in use. Reserve a new buffer and copy the contents
+  // over.
   auto frame =
-      WrapBuffer(std::move(resurrected), expected_format, expected_size);
-  frame->set_color_space(last_delivered_color_space_);
+      ReserveVideoFrame(marked_frame_pixel_format_, marked_frame_size_);
+  // The call to ReserverVideoFrame should not have cleared
+  // |marked_frame_buffer_|, because that buffer is currently in use.
+  DCHECK(marked_frame_buffer_);
+  if (!frame)
+    return nullptr;
+#if DCHECK_IS_ON()
+  // Sanity check that |marked_frame_buffer_| indeed corresponds to a buffer in
+  // |utilized_buffers_|. If MarkFrame() was erroneously called with a frame
+  // that did not belong to this pool or was otherwise tampered with, this might
+  // not be the case.
+  const auto source_it = std::find_if(
+      utilized_buffers_.rbegin(), utilized_buffers_.rend(),
+      [this](const std::pair<const media::VideoFrame*,
+                             base::ReadOnlySharedMemoryRegion>& candidate) {
+        return candidate.first->data(0) == marked_frame_buffer_;
+      });
+  DCHECK(source_it != utilized_buffers_.rend());
+#endif  // DCHECK_IS_ON()
+
+  // Copy the contents over.
+  const size_t num_bytes_to_copy = VideoFrame::AllocationSize(
+      marked_frame_pixel_format_, marked_frame_size_);
+  memcpy(frame->data(0), marked_frame_buffer_, num_bytes_to_copy);
+
+  frame->set_color_space(marked_frame_color_space_);
   return frame;
 }
 
@@ -116,16 +159,8 @@
     const VideoFrame* frame) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
 
-  // Record that this frame is the last-delivered one, for possible future calls
-  // to ResurrectLastVideoFrame().
   const auto it = utilized_buffers_.find(frame);
   DCHECK(it != utilized_buffers_.end());
-  // Assumption: The first image plane's memory pointer should be the start of
-  // the writable mapped memory. WrapBuffer() sanity-checks this.
-  resurrectable_buffer_memory_ = frame->data(0);
-  last_delivered_format_ = frame->format();
-  last_delivered_size_ = frame->coded_size();
-  last_delivered_color_space_ = frame->ColorSpace();
 
   return it->second.Duplicate();
 }
@@ -161,7 +196,7 @@
       static_cast<uint8_t*>(pooled_buffer.mapping.memory()),
       pooled_buffer.mapping.size(), base::TimeDelta());
   DCHECK(frame);
-  // Sanity-check the assumption being made in CloneHandleForDelivery():
+  // Sanity-check the assumption being made for SetMarkedBuffer():
   DCHECK_EQ(frame->data(0), pooled_buffer.mapping.memory());
   utilized_buffers_.emplace(frame.get(), std::move(pooled_buffer.region));
   frame->AddDestructionObserver(
diff --git a/components/viz/service/frame_sinks/video_capture/interprocess_frame_pool.h b/components/viz/service/frame_sinks/video_capture/interprocess_frame_pool.h
index 9337f72..f4ffc14e 100644
--- a/components/viz/service/frame_sinks/video_capture/interprocess_frame_pool.h
+++ b/components/viz/service/frame_sinks/video_capture/interprocess_frame_pool.h
@@ -42,14 +42,20 @@
       media::VideoPixelFormat format,
       const gfx::Size& size);
 
-  // Finds the last VideoFrame delivered, and if it has been returned back to
-  // this pool already, re-materializes it. Otherwise, null is returned. This is
-  // used when the client knows the content of the video frame has not changed
-  // and is trying to avoid having to re-populate a new VideoFrame with the same
-  // content.
-  scoped_refptr<media::VideoFrame> ResurrectLastVideoFrame(
-      media::VideoPixelFormat expected_format,
-      const gfx::Size& expected_size);
+  // Clients may call this using a frame previously returned by
+  // ReserveVideoFrame() to mark it (or its contents) for later resurrection via
+  // ResurrectOrDuplicateContentFromMarkedFrame(). Note that only one frame can
+  // be marked at a time. MarkFrame() will overwrite any existing mark.
+  void MarkFrame(const media::VideoFrame& frame);
+  void ClearFrameMarking();
+  bool HasMarkedFrameWithSize(const gfx::Size& size) const;
+
+  // If no frame is marked, returns nullptr. Otherwise, if the marked frame is
+  // not currently in use, returns the marked frame. If the marked frame is in
+  // use, reserves a new frame and copies the contents of the marked frame to
+  // the newly reserved one. That last case may still return nullptr if the pool
+  // is fully utilized.
+  scoped_refptr<media::VideoFrame> ResurrectOrDuplicateContentFromMarkedFrame();
 
   // Returns a cloned handle to the shared memory backing |frame| and its size
   // in bytes. Note that the client should not allow the ref-count of the
@@ -99,13 +105,12 @@
   base::flat_map<const media::VideoFrame*, base::ReadOnlySharedMemoryRegion>
       utilized_buffers_;
 
-  // The pointer to the mapped memory of the buffer that was last delivered,
-  // along with its format and size. ResurrectLastVideoFrame() uses this
-  // information to locate and confirm that a prior frame can be resurrected.
-  const void* resurrectable_buffer_memory_ = nullptr;
-  media::VideoPixelFormat last_delivered_format_ = media::PIXEL_FORMAT_UNKNOWN;
-  gfx::Size last_delivered_size_;
-  gfx::ColorSpace last_delivered_color_space_;
+  // The pointer to the mapped memory of the buffer that was set as "marked"
+  // via a call to SetMarkedBuffer().
+  const void* marked_frame_buffer_ = nullptr;
+  gfx::Size marked_frame_size_;
+  gfx::ColorSpace marked_frame_color_space_;
+  media::VideoPixelFormat marked_frame_pixel_format_;
 
   // The time at which the last shared memory allocation or mapping failed.
   base::TimeTicks last_fail_log_time_;
diff --git a/components/viz/service/frame_sinks/video_capture/interprocess_frame_pool_unittest.cc b/components/viz/service/frame_sinks/video_capture/interprocess_frame_pool_unittest.cc
index 29e2fee..86233d74 100644
--- a/components/viz/service/frame_sinks/video_capture/interprocess_frame_pool_unittest.cc
+++ b/components/viz/service/frame_sinks/video_capture/interprocess_frame_pool_unittest.cc
@@ -143,70 +143,154 @@
   return true;
 }
 
-TEST(InterprocessFramePoolTest, ResurrectsDeliveredFramesOnly) {
+TEST(InterprocessFramePoolTest, ResurrectFrameThatIsNotInUse) {
   InterprocessFramePool pool(2);
+  const gfx::ColorSpace kArbitraryColorSpace = gfx::ColorSpace::CreateREC709();
 
-  // Reserve a frame, populate it, but release it before delivery.
+  // Reserve a frame, populate it, mark it, and release it.
   scoped_refptr<media::VideoFrame> frame =
       pool.ReserveVideoFrame(kFormat, kSize);
   ASSERT_TRUE(frame);
-  media::FillYUV(frame.get(), 0x11, 0x22, 0x33);
-  frame = nullptr;  // Returns frame to pool.
-
-  // The pool should fail to resurrect the last frame because it was never
-  // delivered.
-  frame = pool.ResurrectLastVideoFrame(kFormat, kSize);
-  ASSERT_FALSE(frame);
-
-  // Reserve a frame and populate it with different color values; only this
-  // time, signal that it will be delivered before releasing it.
-  frame = pool.ReserveVideoFrame(kFormat, kSize);
-  ASSERT_TRUE(frame);
-  const uint8_t kValues[3] = {0x44, 0x55, 0x66};
+  const uint8_t kValues[3] = {0x11, 0x22, 0x33};
   media::FillYUV(frame.get(), kValues[0], kValues[1], kValues[2]);
-  {
-    auto handle = pool.CloneHandleForDelivery(frame.get());
-    ExpectValidHandleForDelivery(handle);
-  }
+  frame->set_color_space(kArbitraryColorSpace);
+  pool.MarkFrame(*frame);
   frame = nullptr;  // Returns frame to pool.
 
-  // Confirm that the last frame can be resurrected repeatedly.
+  ASSERT_TRUE(pool.HasMarkedFrameWithSize(kSize));
+  const gfx::Size kDifferentSize(kSize.width() - 2, kSize.height() + 2);
+  ASSERT_FALSE(pool.HasMarkedFrameWithSize(kDifferentSize));
+
+  // Resurrect the frame and expect it to still have the same content, size,
+  // format, and color space. Release and repeat that a few times.
   for (int i = 0; i < 3; ++i) {
-    frame = pool.ResurrectLastVideoFrame(kFormat, kSize);
+    frame = pool.ResurrectOrDuplicateContentFromMarkedFrame();
     ASSERT_TRUE(frame);
+    ASSERT_EQ(kFormat, frame->format());
+    ASSERT_EQ(kSize, frame->coded_size());
+    ASSERT_EQ(kSize, frame->visible_rect().size());
+    ASSERT_EQ(kSize, frame->natural_size());
+    ASSERT_EQ(kArbitraryColorSpace, frame->ColorSpace());
     ASSERT_TRUE(PlanesAreFilledWithValues(*frame, kValues));
-    frame = nullptr;  // Returns frame to pool.
+    frame = nullptr;
   }
+}
 
-  // A frame that is being delivered cannot be resurrected.
-  for (int i = 0; i < 2; ++i) {
-    if (i == 0) {  // Test this for a resurrected frame.
-      frame = pool.ResurrectLastVideoFrame(kFormat, kSize);
-      ASSERT_TRUE(frame);
-      ASSERT_TRUE(PlanesAreFilledWithValues(*frame, kValues));
-    } else {  // Test this for a normal frame.
-      frame = pool.ReserveVideoFrame(kFormat, kSize);
-      ASSERT_TRUE(frame);
-      media::FillYUV(frame.get(), 0x77, 0x88, 0x99);
-    }
-    {
-      auto handle = pool.CloneHandleForDelivery(frame.get());
-      ExpectValidHandleForDelivery(handle);
-    }
-    scoped_refptr<media::VideoFrame> should_be_null =
-        pool.ResurrectLastVideoFrame(kFormat, kSize);
-    ASSERT_FALSE(should_be_null);
-    frame = nullptr;  // Returns frame to pool.
-  }
+TEST(InterprocessFramePoolTest, ResurrectContentFromFrameThatIsStillInUse) {
+  InterprocessFramePool pool(2);
+  const gfx::ColorSpace kArbitraryColorSpace = gfx::ColorSpace::CreateREC709();
 
-  // Finally, reserve a frame, populate it, and don't deliver it. Expect that,
-  // still, undelivered frames cannot be resurrected.
-  frame = pool.ReserveVideoFrame(kFormat, kSize);
+  // Reserve a frame, populate it, mark it, and hold on to it.
+  scoped_refptr<media::VideoFrame> frame =
+      pool.ReserveVideoFrame(kFormat, kSize);
   ASSERT_TRUE(frame);
-  media::FillYUV(frame.get(), 0xaa, 0xbb, 0xcc);
-  frame = nullptr;  // Returns frame to pool.
-  frame = pool.ResurrectLastVideoFrame(kFormat, kSize);
+  const uint8_t kValues[3] = {0x11, 0x22, 0x33};
+  media::FillYUV(frame.get(), kValues[0], kValues[1], kValues[2]);
+  frame->set_color_space(kArbitraryColorSpace);
+  pool.MarkFrame(*frame);
+
+  ASSERT_TRUE(pool.HasMarkedFrameWithSize(kSize));
+  const gfx::Size kDifferentSize(kSize.width() - 2, kSize.height() + 2);
+  ASSERT_FALSE(pool.HasMarkedFrameWithSize(kDifferentSize));
+
+  scoped_refptr<media::VideoFrame> frame2 =
+      pool.ResurrectOrDuplicateContentFromMarkedFrame();
+  ASSERT_TRUE(frame2);
+  ASSERT_NE(frame, frame2);
+  ASSERT_NE(frame->data(0), frame2->data(0));
+  ASSERT_EQ(kFormat, frame2->format());
+  ASSERT_EQ(kSize, frame2->coded_size());
+  ASSERT_EQ(kSize, frame2->visible_rect().size());
+  ASSERT_EQ(kSize, frame2->natural_size());
+  ASSERT_EQ(kArbitraryColorSpace, frame2->ColorSpace());
+  ASSERT_TRUE(PlanesAreFilledWithValues(*frame2, kValues));
+}
+
+TEST(InterprocessFramePoolTest, ResurrectWhenAtCapacity) {
+  InterprocessFramePool pool(2);
+  const gfx::ColorSpace kArbitraryColorSpace = gfx::ColorSpace::CreateREC709();
+
+  // Reserve two frames and hold on to them
+  scoped_refptr<media::VideoFrame> frame1 =
+      pool.ReserveVideoFrame(kFormat, kSize);
+  scoped_refptr<media::VideoFrame> frame2 =
+      pool.ReserveVideoFrame(kFormat, kSize);
+  ASSERT_TRUE(frame1);
+  ASSERT_TRUE(frame2);
+  // Fill and mark one of them
+  const uint8_t kValues[3] = {0x11, 0x22, 0x33};
+  media::FillYUV(frame1.get(), kValues[0], kValues[1], kValues[2]);
+  frame1->set_color_space(kArbitraryColorSpace);
+  pool.MarkFrame(*frame1);
+
+  // Attempt to resurrect. This should fail, because the pool is already at
+  // capacity.
+  scoped_refptr<media::VideoFrame> frame3 =
+      pool.ResurrectOrDuplicateContentFromMarkedFrame();
+  ASSERT_FALSE(frame3);
+
+  // Release the first frame
+  frame1 = nullptr;
+
+  // Now, resurrecting should work again.
+  frame3 = pool.ResurrectOrDuplicateContentFromMarkedFrame();
+  ASSERT_TRUE(frame3);
+  ASSERT_EQ(kFormat, frame3->format());
+  ASSERT_EQ(kSize, frame3->coded_size());
+  ASSERT_EQ(kArbitraryColorSpace, frame3->ColorSpace());
+  ASSERT_TRUE(PlanesAreFilledWithValues(*frame3, kValues));
+}
+
+TEST(InterprocessFramePoolTest, ResurrectWhenNoFrameMarked) {
+  InterprocessFramePool pool(2);
+
+  // Attempt to resurrect before any frame was ever reserved.
+  scoped_refptr<media::VideoFrame> frame =
+      pool.ResurrectOrDuplicateContentFromMarkedFrame();
   ASSERT_FALSE(frame);
+
+  // Reserve a frame and release it without marking it.
+  scoped_refptr<media::VideoFrame> frame2 =
+      pool.ReserveVideoFrame(kFormat, kSize);
+  ASSERT_TRUE(frame2);
+  frame2 = nullptr;  // Returns frame to pool.
+
+  // Attempt to resurrect. This should fail, because no frame was marked.
+  scoped_refptr<media::VideoFrame> frame3 =
+      pool.ResurrectOrDuplicateContentFromMarkedFrame();
+  ASSERT_FALSE(frame3);
+}
+
+TEST(InterprocessFramePoolTest, FrameMarkingIsLostWhenBufferIsReallocated) {
+  InterprocessFramePool pool(2);
+
+  // Reserve enough frames to hit capacity.
+  scoped_refptr<media::VideoFrame> frame1 =
+      pool.ReserveVideoFrame(kFormat, kSize);
+  scoped_refptr<media::VideoFrame> frame2 =
+      pool.ReserveVideoFrame(kFormat, kSize);
+  ASSERT_TRUE(frame1);
+  ASSERT_TRUE(frame2);
+
+  // Mark one of them
+  pool.MarkFrame(*frame1);
+  ASSERT_TRUE(pool.HasMarkedFrameWithSize(kSize));
+
+  // Release all frames
+  frame1 = nullptr;
+  frame2 = nullptr;
+
+  // Reserve all frames again but this time request a bigger size.
+  // This should lead to all buffers being reallocated and the marking being
+  // lost.
+  gfx::Size kBiggerSize(kSize.width() + 2, kSize.height() + 2);
+  frame1 = pool.ReserveVideoFrame(kFormat, kBiggerSize);
+  frame2 = pool.ReserveVideoFrame(kFormat, kBiggerSize);
+  ASSERT_TRUE(frame1);
+  ASSERT_TRUE(frame2);
+
+  ASSERT_FALSE(pool.HasMarkedFrameWithSize(kSize));
+  ASSERT_FALSE(pool.HasMarkedFrameWithSize(kBiggerSize));
 }
 
 TEST(InterprocessFramePoolTest, ReportsCorrectUtilization) {
@@ -219,7 +303,7 @@
     // Reserve the frame and expect 1/2 the pool to be utilized.
     scoped_refptr<media::VideoFrame> frame =
         (i == 0) ? pool.ReserveVideoFrame(kFormat, kSize)
-                 : pool.ResurrectLastVideoFrame(kFormat, kSize);
+                 : pool.ResurrectOrDuplicateContentFromMarkedFrame();
     ASSERT_TRUE(frame);
     ASSERT_EQ(0.5f, pool.GetUtilization());
 
@@ -231,6 +315,9 @@
     }
     ASSERT_EQ(0.5f, pool.GetUtilization());
 
+    // Mark the frame for later resurrection.
+    pool.MarkFrame(*frame);
+
     // Finally, release the frame to indicate it has been delivered and is no
     // longer in-use by downstream consumers. This should cause the utilization
     // to go back down to zero.
diff --git a/components/viz/service/surfaces/surface.cc b/components/viz/service/surfaces/surface.cc
index 01a6440..a31c903 100644
--- a/components/viz/service/surfaces/surface.cc
+++ b/components/viz/service/surfaces/surface.cc
@@ -521,6 +521,14 @@
   blocking_allocation_groups_.clear();
   activation_dependencies_.clear();
 
+  // If the client has specified a deadline of zero and we don't need to block
+  // on the parent, there is no need to figure out the activation dependencies,
+  // since the frame will activate immediately.
+  bool block_activation =
+      block_activation_on_parent_ && !seen_first_surface_dependency_;
+  if (!block_activation && current_frame.metadata.deadline.IsZero())
+    return;
+
   std::vector<SurfaceAllocationGroup*> new_blocking_allocation_groups;
   std::vector<SurfaceId> new_activation_dependencies;
   for (const SurfaceId& surface_id :
diff --git a/content/browser/background_sync/background_sync_manager.cc b/content/browser/background_sync/background_sync_manager.cc
index 4d147306..d4ab24d 100644
--- a/content/browser/background_sync/background_sync_manager.cc
+++ b/content/browser/background_sync/background_sync_manager.cc
@@ -39,6 +39,8 @@
 #include "content/browser/android/background_sync_network_observer_android.h"
 #endif
 
+using blink::mojom::BackgroundSyncType;
+
 namespace content {
 
 namespace {
@@ -192,10 +194,27 @@
   std::move(task).Run(std::move(callback));
 }
 
-blink::mojom::BackgroundSyncType GetBackgroundSyncType(
+BackgroundSyncType GetBackgroundSyncType(
     const blink::mojom::SyncRegistrationOptions& options) {
-  return options.min_interval >= 0 ? blink::mojom::BackgroundSyncType::PERIODIC
-                                   : blink::mojom::BackgroundSyncType::ONE_SHOT;
+  return options.min_interval >= 0 ? BackgroundSyncType::PERIODIC
+                                   : BackgroundSyncType::ONE_SHOT;
+}
+
+std::string GetEventStatusString(blink::ServiceWorkerStatusCode status_code) {
+  // The |status_code| is derived from blink::mojom::ServiceWorkerEventStatus.
+  switch (status_code) {
+    case blink::ServiceWorkerStatusCode::kOk:
+      return "succeeded";
+    case blink::ServiceWorkerStatusCode::kErrorEventWaitUntilRejected:
+      return "waitUntil rejected";
+    case blink::ServiceWorkerStatusCode::kErrorAbort:
+      return "aborted";
+    case blink::ServiceWorkerStatusCode::kErrorTimeout:
+      return "timeout";
+    default:
+      NOTREACHED();
+      return "unknown error";
+  }
 }
 
 }  // namespace
@@ -450,10 +469,10 @@
 
       for (const auto& registration_proto :
            registrations_proto.registration()) {
-        blink::mojom::BackgroundSyncType sync_type =
+        BackgroundSyncType sync_type =
             registration_proto.has_periodic_sync_options()
-                ? blink::mojom::BackgroundSyncType::PERIODIC
-                : blink::mojom::BackgroundSyncType::ONE_SHOT;
+                ? BackgroundSyncType::PERIODIC
+                : BackgroundSyncType::ONE_SHOT;
         BackgroundSyncRegistration* registration =
             &registrations
                  ->registration_map[{registration_proto.tag(), sync_type}];
@@ -461,7 +480,7 @@
         blink::mojom::SyncRegistrationOptions* options =
             registration->options();
         options->tag = registration_proto.tag();
-        if (sync_type == blink::mojom::BackgroundSyncType::PERIODIC) {
+        if (sync_type == BackgroundSyncType::PERIODIC) {
           options->min_interval =
               registration_proto.periodic_sync_options().min_interval();
           if (options->min_interval < 0) {
@@ -617,8 +636,7 @@
 
   *new_registration.options() = std::move(options);
 
-  if (new_registration.sync_type() ==
-      blink::mojom::BackgroundSyncType::PERIODIC) {
+  if (new_registration.sync_type() == BackgroundSyncType::PERIODIC) {
     base::PostTaskWithTraitsAndReplyWithResult(
         FROM_HERE, {BrowserThread::UI},
         base::BindOnce(
@@ -643,8 +661,7 @@
 
   // For one-shot registrations, we let the delay_until be in the past, so that
   // an event is fired at the soonest opportune moment.
-  if (new_registration.sync_type() ==
-      blink::mojom::BackgroundSyncType::PERIODIC) {
+  if (new_registration.sync_type() == BackgroundSyncType::PERIODIC) {
     new_registration.set_delay_until(clock_->Now() + delay);
   }
 
@@ -858,13 +875,12 @@
       &active_registrations_[sw_registration_id];
   registrations->origin = origin;
 
-  blink::mojom::BackgroundSyncType sync_type = sync_registration.sync_type();
+  BackgroundSyncType sync_type = sync_registration.sync_type();
   registrations
       ->registration_map[{sync_registration.options()->tag, sync_type}] =
       sync_registration;
 
-  if (devtools_context_->IsRecording(devtools::proto::BACKGROUND_SYNC) &&
-      sync_type == blink::mojom::BackgroundSyncType::ONE_SHOT) {
+  if (ShouldLogToDevTools(sync_type)) {
     devtools_context_->LogBackgroundServiceEvent(
         sw_registration_id, origin, devtools::proto::BACKGROUND_SYNC,
         /* event_name= */ "registered sync",
@@ -1010,7 +1026,7 @@
 }
 
 base::TimeDelta BackgroundSyncManager::GetSoonestWakeupDelta(
-    blink::mojom::BackgroundSyncType sync_type) {
+    BackgroundSyncType sync_type) {
   DCHECK_CURRENTLY_ON(BrowserThread::IO);
   base::TimeDelta soonest_wakeup_delta = base::TimeDelta::Max();
   for (const auto& sw_reg_id_and_registrations : active_registrations_) {
@@ -1036,7 +1052,7 @@
 
   // If the browser is closed while firing events, the browser needs a task to
   // wake it back up and try again.
-  if (sync_type == blink::mojom::BackgroundSyncType::ONE_SHOT &&
+  if (sync_type == BackgroundSyncType::ONE_SHOT &&
       num_firing_registrations_ > 0 &&
       soonest_wakeup_delta > parameters_->min_sync_recovery_time) {
     soonest_wakeup_delta = parameters_->min_sync_recovery_time;
@@ -1048,9 +1064,9 @@
 void BackgroundSyncManager::RunInBackgroundIfNecessary() {
   DCHECK_CURRENTLY_ON(BrowserThread::IO);
 
-  base::TimeDelta soonest_wakeup_delta = std::min(
-      GetSoonestWakeupDelta(blink::mojom::BackgroundSyncType::ONE_SHOT),
-      GetSoonestWakeupDelta(blink::mojom::BackgroundSyncType::PERIODIC));
+  base::TimeDelta soonest_wakeup_delta =
+      std::min(GetSoonestWakeupDelta(BackgroundSyncType::ONE_SHOT),
+               GetSoonestWakeupDelta(BackgroundSyncType::PERIODIC));
 
   // Try firing again after the wakeup delta.
   if (!soonest_wakeup_delta.is_max() && !soonest_wakeup_delta.is_zero()) {
@@ -1180,8 +1196,8 @@
   // Don't dispatch a sync event if the sync is periodic.
   // TODO(crbug.com/925297): Remove this code when we've added the logic to
   // dispatch periodic sync events.
-  if (registration && registration_info->sync_type ==
-                          blink::mojom::BackgroundSyncType::PERIODIC) {
+  if (registration &&
+      registration_info->sync_type == BackgroundSyncType::PERIODIC) {
     RemoveActiveRegistration(*registration_info);
     StoreRegistrations(registration_info->service_worker_registration_id,
                        base::DoNothing());
@@ -1291,8 +1307,7 @@
 
   // It's important to update |num_attempts| before we update |delay_until|.
   registration->set_num_attempts(registration->num_attempts() + 1);
-  if ((registration->sync_type() ==
-           blink::mojom::BackgroundSyncType::PERIODIC &&
+  if ((registration->sync_type() == BackgroundSyncType::PERIODIC &&
        registration->num_attempts() == parameters_->max_sync_attempts) ||
       (registration->sync_state() ==
        blink::mojom::BackgroundSyncState::REREGISTERED_WHILE_FIRING)) {
@@ -1301,7 +1316,7 @@
 
   // If |delay_until| needs to be updated, get updated delay.
   bool succeeded = status_code == blink::ServiceWorkerStatusCode::kOk;
-  if (registration->sync_type() == blink::mojom::BackgroundSyncType::PERIODIC ||
+  if (registration->sync_type() == BackgroundSyncType::PERIODIC ||
       (!succeeded &&
        registration->num_attempts() < parameters_->max_sync_attempts)) {
     base::PostTaskWithTraitsAndReplyWithResult(
@@ -1311,18 +1326,18 @@
             std::make_unique<BackgroundSyncParameters>(*parameters_)),
         base::BindOnce(&BackgroundSyncManager::EventCompleteDidGetDelay,
                        weak_ptr_factory_.GetWeakPtr(),
-                       std::move(registration_info), succeeded, origin,
+                       std::move(registration_info), status_code, origin,
                        std::move(callback)));
     return;
   }
 
-  EventCompleteDidGetDelay(std::move(registration_info), succeeded, origin,
+  EventCompleteDidGetDelay(std::move(registration_info), status_code, origin,
                            std::move(callback), base::TimeDelta::Max());
 }
 
 void BackgroundSyncManager::EventCompleteDidGetDelay(
     blink::mojom::BackgroundSyncRegistrationInfoPtr registration_info,
-    bool succeeded,
+    blink::ServiceWorkerStatusCode status_code,
     const url::Origin& origin,
     base::OnceClosure callback,
     base::TimeDelta delay) {
@@ -1336,51 +1351,59 @@
     return;
   }
 
+  bool succeeded = status_code == blink::ServiceWorkerStatusCode::kOk;
   bool can_retry =
       registration->num_attempts() < parameters_->max_sync_attempts;
 
-  if (devtools_context_->IsRecording(devtools::proto::BACKGROUND_SYNC) &&
-      registration_info->sync_type ==
-          blink::mojom::BackgroundSyncType::ONE_SHOT) {
-    if (succeeded) {
-      devtools_context_->LogBackgroundServiceEvent(
-          registration_info->service_worker_registration_id, origin,
-          devtools::proto::BACKGROUND_SYNC,
-          /* event_name= */ "sync event failed",
-          /* instance_id= */ registration_info->tag,
-          /* event_metadata= */
-          {{"can_retry", can_retry ? "yes" : "no"},
-           {"next attempt delay (ms)",
-            delay.is_max() ? "infinite"
-                           : base::NumberToString(delay.InMilliseconds())}});
-    } else {
-      devtools_context_->LogBackgroundServiceEvent(
-          registration_info->service_worker_registration_id, origin,
-          devtools::proto::BACKGROUND_SYNC,
-          /* event_name= */ "sync complete",
-          /* instance_id= */ registration_info->tag,
-          /* event_metadata= */
-          {{"succeeded", succeeded ? "yes" : "no"}});
-    }
-  }
-
   bool registration_completed = true;
-
   if (registration->sync_state() ==
       blink::mojom::BackgroundSyncState::REREGISTERED_WHILE_FIRING) {
     registration->set_sync_state(blink::mojom::BackgroundSyncState::PENDING);
     registration->set_num_attempts(0);
     registration_completed = false;
-  } else if (registration->sync_type() ==
-                 blink::mojom::BackgroundSyncType::PERIODIC ||
-             (!succeeded && can_retry)) {
+    if (ShouldLogToDevTools(registration->sync_type())) {
+      devtools_context_->LogBackgroundServiceEvent(
+          registration_info->service_worker_registration_id, origin,
+          devtools::proto::BACKGROUND_SYNC,
+          /* event_name= */ "sync event reregistered",
+          /* instance_id= */ registration_info->tag,
+          /* event_metadata= */ {});
+    }
+  } else if ((!succeeded && can_retry) ||
+             registration->sync_type() == BackgroundSyncType::PERIODIC) {
     registration->set_sync_state(blink::mojom::BackgroundSyncState::PENDING);
     registration_completed = false;
     registration->set_delay_until(clock_->Now() + delay);
+
+    if (ShouldLogToDevTools(registration->sync_type())) {
+      std::string delay_ms = delay.is_max()
+                                 ? "infinite"
+                                 : base::NumberToString(delay.InMilliseconds());
+      devtools_context_->LogBackgroundServiceEvent(
+          registration_info->service_worker_registration_id, origin,
+          devtools::proto::BACKGROUND_SYNC,
+          /* event_name= */ "sync event failed",
+          /* instance_id= */ registration_info->tag,
+          {{"next attempt delay (ms)", delay_ms},
+           {"failure reason", GetEventStatusString(status_code)}});
+    }
   }
 
-  if (registration_completed)
+  if (registration_completed) {
+    BackgroundSyncMetrics::RecordRegistrationComplete(
+        succeeded, registration->num_attempts());
+
+    if (ShouldLogToDevTools(registration->sync_type())) {
+      devtools_context_->LogBackgroundServiceEvent(
+          registration_info->service_worker_registration_id, origin,
+          devtools::proto::BACKGROUND_SYNC,
+          /* event_name= */ "sync complete",
+          /* instance_id= */ registration_info->tag,
+          {{"status", GetEventStatusString(status_code)}});
+    }
+
     RemoveActiveRegistration(*registration_info);
+  }
 
   StoreRegistrations(
       registration_info->service_worker_registration_id,
@@ -1465,13 +1488,18 @@
 blink::ServiceWorkerStatusCode BackgroundSyncManager::CanEmulateSyncEvent(
     scoped_refptr<ServiceWorkerVersion> active_version) {
   if (!active_version)
-    return blink::ServiceWorkerStatusCode::kErrorFailed;
+    return blink::ServiceWorkerStatusCode::kErrorAbort;
   if (!network_observer_->NetworkSufficient())
-    return blink::ServiceWorkerStatusCode::kErrorNetwork;
+    return blink::ServiceWorkerStatusCode::kErrorEventWaitUntilRejected;
   int64_t registration_id = active_version->registration_id();
   if (base::ContainsKey(emulated_offline_sw_, registration_id))
-    return blink::ServiceWorkerStatusCode::kErrorNetwork;
+    return blink::ServiceWorkerStatusCode::kErrorEventWaitUntilRejected;
   return blink::ServiceWorkerStatusCode::kOk;
 }
 
+bool BackgroundSyncManager::ShouldLogToDevTools(BackgroundSyncType sync_type) {
+  return sync_type == BackgroundSyncType::ONE_SHOT &&
+         devtools_context_->IsRecording(devtools::proto::BACKGROUND_SYNC);
+}
+
 }  // namespace content
diff --git a/content/browser/background_sync/background_sync_manager.h b/content/browser/background_sync/background_sync_manager.h
index 11723924..8ca76b85 100644
--- a/content/browser/background_sync/background_sync_manager.h
+++ b/content/browser/background_sync/background_sync_manager.h
@@ -287,7 +287,7 @@
       base::OnceClosure callback);
   void EventCompleteDidGetDelay(
       blink::mojom::BackgroundSyncRegistrationInfoPtr registration_info,
-      bool succeeded,
+      blink::ServiceWorkerStatusCode status_code,
       const url::Origin& origin,
       base::OnceClosure callback,
       base::TimeDelta delay);
@@ -312,6 +312,9 @@
   void SetMaxSyncAttemptsImpl(int max_sync_attempts,
                               base::OnceClosure callback);
 
+  // Whether an event should be logged for debuggability.
+  bool ShouldLogToDevTools(blink::mojom::BackgroundSyncType sync_type);
+
   base::OnceClosure MakeEmptyCompletion();
 
   blink::ServiceWorkerStatusCode CanEmulateSyncEvent(
diff --git a/content/browser/background_sync/background_sync_manager_unittest.cc b/content/browser/background_sync/background_sync_manager_unittest.cc
index 5a0034b..6b7a73f 100644
--- a/content/browser/background_sync/background_sync_manager_unittest.cc
+++ b/content/browser/background_sync/background_sync_manager_unittest.cc
@@ -402,7 +402,8 @@
   void InitFailedSyncEventTest() {
     SetupForSyncEvent(base::BindRepeating(
         &BackgroundSyncManagerTest::DispatchSyncStatusCallback,
-        base::Unretained(this), blink::ServiceWorkerStatusCode::kErrorFailed));
+        base::Unretained(this),
+        blink::ServiceWorkerStatusCode::kErrorEventWaitUntilRejected));
   }
 
   void DispatchSyncDelayedCallback(
@@ -913,7 +914,7 @@
   // The first sync attempt fails.
   ASSERT_TRUE(sync_fired_callback_);
   std::move(sync_fired_callback_)
-      .Run(blink::ServiceWorkerStatusCode::kErrorFailed);
+      .Run(blink::ServiceWorkerStatusCode::kErrorEventWaitUntilRejected);
   base::RunLoop().RunUntilIdle();
 
   // It should fire again since it was reregistered mid-sync.
@@ -1547,7 +1548,7 @@
   InitSyncEventTest();
   bool was_called = false;
   blink::ServiceWorkerStatusCode code =
-      blink::ServiceWorkerStatusCode::kErrorFailed;
+      blink::ServiceWorkerStatusCode::kErrorEventWaitUntilRejected;
   background_sync_manager_->EmulateDispatchSyncEvent(
       "emulated_tag", sw_registration_1_->active_version(), false,
       base::BindOnce(EmulateDispatchSyncEventCallback, &was_called, &code));
@@ -1566,7 +1567,7 @@
       base::BindOnce(EmulateDispatchSyncEventCallback, &was_called, &code));
   base::RunLoop().RunUntilIdle();
   EXPECT_TRUE(was_called);
-  EXPECT_EQ(blink::ServiceWorkerStatusCode::kErrorNetwork, code);
+  EXPECT_EQ(blink::ServiceWorkerStatusCode::kErrorEventWaitUntilRejected, code);
 
   background_sync_manager_->EmulateServiceWorkerOffline(sw_registration_id_1_,
                                                         false);
@@ -1579,7 +1580,7 @@
       base::BindOnce(EmulateDispatchSyncEventCallback, &was_called, &code));
   base::RunLoop().RunUntilIdle();
   EXPECT_TRUE(was_called);
-  EXPECT_EQ(blink::ServiceWorkerStatusCode::kErrorNetwork, code);
+  EXPECT_EQ(blink::ServiceWorkerStatusCode::kErrorEventWaitUntilRejected, code);
 
   SetNetwork(network::mojom::ConnectionType::CONNECTION_WIFI);
   was_called = false;
@@ -1633,4 +1634,45 @@
   }
 }
 
+TEST_F(BackgroundSyncManagerTest, UkmRecordedAtCompletion) {
+  InitSyncEventTest();
+  {
+    base::HistogramTester histogram_tester;
+
+    EXPECT_TRUE(Register(sync_options_1_));
+
+    test_background_sync_manager_->RunDelayedTask();
+    base::RunLoop().RunUntilIdle();
+
+    EXPECT_FALSE(GetRegistration(sync_options_1_));
+
+    histogram_tester.ExpectBucketCount(
+        "BackgroundSync.Registration.OneShot.EventSucceededAtCompletion", true,
+        1);
+    histogram_tester.ExpectBucketCount(
+        "BackgroundSync.Registration.OneShot.NumAttemptsForSuccessfulEvent", 1,
+        1);
+  }
+
+  SetMaxSyncAttemptsAndRestartManager(1);
+  InitFailedSyncEventTest();
+  {
+    base::HistogramTester histogram_tester;
+
+    EXPECT_TRUE(Register(sync_options_2_));
+
+    test_background_sync_manager_->RunDelayedTask();
+    base::RunLoop().RunUntilIdle();
+
+    EXPECT_FALSE(GetRegistration(sync_options_2_));
+
+    histogram_tester.ExpectBucketCount(
+        "BackgroundSync.Registration.OneShot.EventSucceededAtCompletion", false,
+        1);
+    histogram_tester.ExpectBucketCount(
+        "BackgroundSync.Registration.OneShot.NumAttemptsForSuccessfulEvent", 1,
+        0);
+  }
+}
+
 }  // namespace content
diff --git a/content/browser/child_process_launcher_helper.cc b/content/browser/child_process_launcher_helper.cc
index 8cbede7e..956df48 100644
--- a/content/browser/child_process_launcher_helper.cc
+++ b/content/browser/child_process_launcher_helper.cc
@@ -227,13 +227,22 @@
       launcher_task_runner(
           android::LauncherThread::GetMessageLoop()->task_runner());
   return (*launcher_task_runner).get();
-#else   // defined(OS_ANDROID)
+#else  // defined(OS_ANDROID)
+  constexpr base::TaskShutdownBehavior shutdown_behavior =
+#if defined(OS_WIN)
+      base::TaskShutdownBehavior::CONTINUE_ON_SHUTDOWN;
+#else
+      // Linux could use CONTINUE_ON_SHUTDOWN if ZygoteHostImpl was leaked on
+      // shutdown. Mac could use CONTINUE_ON_SHUTDOWN if PluginServiceImpl was
+      // leaked on shutdown.
+      base::TaskShutdownBehavior::BLOCK_SHUTDOWN;
+#endif  // defined(OS_WIN)
   // TODO(http://crbug.com/820200): Investigate whether we could use
   // SequencedTaskRunner on platforms other than Windows.
   static base::LazySingleThreadTaskRunner launcher_task_runner =
       LAZY_SINGLE_THREAD_TASK_RUNNER_INITIALIZER(
           base::TaskTraits({base::MayBlock(), base::TaskPriority::USER_BLOCKING,
-                            base::TaskShutdownBehavior::BLOCK_SHUTDOWN}),
+                            shutdown_behavior}),
           base::SingleThreadTaskRunnerThreadMode::DEDICATED);
   return launcher_task_runner.Get().get();
 #endif  // defined(OS_ANDROID)
diff --git a/content/browser/image_capture/OWNERS b/content/browser/image_capture/OWNERS
index c261502..cc7f7bb 100644
--- a/content/browser/image_capture/OWNERS
+++ b/content/browser/image_capture/OWNERS
@@ -1,5 +1,7 @@
-mcasas@chromium.org
 miu@chromium.org
 
+# Original (legacy) owner.
+mcasas@chromium.org
+
 # COMPONENT: Blink>ImageCapture
-# TEAM: media-dev@chromium.org
+# TEAM: webrtc-dev@chromium.org
diff --git a/content/browser/portal/portal_browsertest.cc b/content/browser/portal/portal_browsertest.cc
index 37f294f..07cdd163a 100644
--- a/content/browser/portal/portal_browsertest.cc
+++ b/content/browser/portal/portal_browsertest.cc
@@ -4,6 +4,7 @@
 
 #include "base/bind.h"
 #include "base/callback.h"
+#include "base/test/bind_test_util.h"
 #include "base/test/scoped_feature_list.h"
 #include "build/build_config.h"
 #include "content/browser/frame_host/render_frame_host_impl.h"
@@ -426,4 +427,59 @@
   EXPECT_EQ(false, EvalJs(portal_frame, "clicked"));
 }
 
+// Tests that async hit testing does not target portals.
+IN_PROC_BROWSER_TEST_F(PortalBrowserTest, AsyncEventTargetingIgnoresPortals) {
+  EXPECT_TRUE(NavigateToURL(
+      shell(), embedded_test_server()->GetURL("portal.test", "/title1.html")));
+  WebContentsImpl* web_contents_impl =
+      static_cast<WebContentsImpl*>(shell()->web_contents());
+  RenderFrameHostImpl* main_frame = web_contents_impl->GetMainFrame();
+
+  // Create portal and wait for navigation.
+  PortalCreatedObserver portal_created_observer(main_frame);
+  GURL a_url(embedded_test_server()->GetURL("a.com", "/title1.html"));
+  EXPECT_TRUE(ExecJs(main_frame,
+                     JsReplace("var portal = document.createElement('portal');"
+                               "portal.src = $1;"
+                               "document.body.appendChild(portal);",
+                               a_url)));
+  Portal* portal = portal_created_observer.WaitUntilPortalCreated();
+  WebContentsImpl* portal_contents = portal->GetPortalContents();
+  RenderFrameHostImpl* portal_frame = portal_contents->GetMainFrame();
+  ASSERT_TRUE(static_cast<RenderWidgetHostViewBase*>(portal_frame->GetView())
+                  ->IsRenderWidgetHostViewChildFrame());
+  RenderWidgetHostViewChildFrame* portal_view =
+      static_cast<RenderWidgetHostViewChildFrame*>(portal_frame->GetView());
+  TestNavigationObserver navigation_observer(portal_contents);
+  navigation_observer.Wait();
+  WaitForHitTestDataOrChildSurfaceReady(portal_frame);
+
+  viz::mojom::InputTargetClient* target_client =
+      main_frame->GetRenderWidgetHost()->input_target_client();
+  ASSERT_TRUE(target_client);
+
+  gfx::PointF root_location =
+      portal_view->TransformPointToRootCoordSpaceF(gfx::PointF(5, 5));
+
+  // Query the renderer for the target widget. The root should claim the point
+  // for itself, not the portal.
+  base::RunLoop run_loop;
+  base::OnceClosure quit_closure = run_loop.QuitClosure();
+  viz::FrameSinkId received_frame_sink_id;
+  target_client->FrameSinkIdAt(
+      root_location, 0,
+      base::BindLambdaForTesting(
+          [&](const viz::FrameSinkId& id, const gfx::PointF& point) {
+            received_frame_sink_id = id;
+            std::move(quit_closure).Run();
+          }));
+  run_loop.Run();
+
+  viz::FrameSinkId root_frame_sink_id =
+      static_cast<RenderWidgetHostViewBase*>(main_frame->GetView())
+          ->GetFrameSinkId();
+  EXPECT_EQ(root_frame_sink_id, received_frame_sink_id)
+      << "Note: The portal's FrameSinkId is " << portal_view->GetFrameSinkId();
+}
+
 }  // namespace content
diff --git a/content/browser/renderer_host/frame_connector_delegate.cc b/content/browser/renderer_host/frame_connector_delegate.cc
index 39f910a..c0d82a9 100644
--- a/content/browser/renderer_host/frame_connector_delegate.cc
+++ b/content/browser/renderer_host/frame_connector_delegate.cc
@@ -48,7 +48,9 @@
   render_widget_host->SetAutoResize(visual_properties.auto_resize_enabled,
                                     visual_properties.min_size_for_auto_resize,
                                     visual_properties.max_size_for_auto_resize);
-  render_widget_host->SetPageScaleFactor(visual_properties.page_scale_factor);
+  render_widget_host->SetPageScaleState(
+      visual_properties.page_scale_factor,
+      visual_properties.is_pinch_gesture_active);
 
   render_widget_host->SynchronizeVisualProperties();
 }
diff --git a/content/browser/renderer_host/input/input_router_impl.cc b/content/browser/renderer_host/input/input_router_impl.cc
index 8ffc145..09fddbc 100644
--- a/content/browser/renderer_host/input/input_router_impl.cc
+++ b/content/browser/renderer_host/input/input_router_impl.cc
@@ -57,16 +57,13 @@
 
 std::unique_ptr<InputEvent> ScaleEvent(const WebInputEvent& event,
                                        double scale,
-                                       const ui::LatencyInfo latency_info) {
+                                       const ui::LatencyInfo& latency_info) {
   std::unique_ptr<blink::WebInputEvent> event_in_viewport =
       ui::ScaleWebInputEvent(event, scale);
   if (event_in_viewport) {
-    ui::LatencyInfo scaled_latency_info(latency_info);
-    scaled_latency_info.set_scroll_update_delta(
-        latency_info.scroll_update_delta() * scale);
     return std::make_unique<InputEvent>(
         ui::WebScopedInputEvent(event_in_viewport.release()),
-        scaled_latency_info);
+        latency_info.ScaledBy(scale));
   }
 
   return std::make_unique<InputEvent>(ui::WebInputEventTraits::Clone(event),
diff --git a/content/browser/renderer_host/input/render_widget_host_latency_tracker.cc b/content/browser/renderer_host/input/render_widget_host_latency_tracker.cc
index e9b9352..ec4452bc 100644
--- a/content/browser/renderer_host/input/render_widget_host_latency_tracker.cc
+++ b/content/browser/renderer_host/input/render_widget_host_latency_tracker.cc
@@ -174,6 +174,8 @@
     has_seen_first_gesture_scroll_update_ = true;
     latency->set_scroll_update_delta(
         static_cast<const WebGestureEvent&>(event).data.scroll_update.delta_y);
+    latency->set_predicted_scroll_update_delta(
+        static_cast<const WebGestureEvent&>(event).data.scroll_update.delta_y);
   }
 }
 
diff --git a/content/browser/renderer_host/media/OWNERS b/content/browser/renderer_host/media/OWNERS
index 68a9bdc..107a4d64 100644
--- a/content/browser/renderer_host/media/OWNERS
+++ b/content/browser/renderer_host/media/OWNERS
@@ -7,8 +7,10 @@
 olka@chromium.org
 maxmorin@chromium.org
 
-per-file *video*=mcasas@chromium.org
 per-file *video*=chfremer@chromium.org
 
-# TEAM: media-capture-and-streams@grotations.appspotmail.com
+# Original (legacy) owner.
+per-file *video*=mcasas@chromium.org
+
 # COMPONENT: Blink>GetUserMedia
+# TEAM: webrtc-dev@chromium.org
diff --git a/content/browser/renderer_host/render_widget_host_impl.cc b/content/browser/renderer_host/render_widget_host_impl.cc
index c9a555e7..edfc500c 100644
--- a/content/browser/renderer_host/render_widget_host_impl.cc
+++ b/content/browser/renderer_host/render_widget_host_impl.cc
@@ -422,6 +422,7 @@
       visual_properties_ack_pending_(false),
       auto_resize_enabled_(false),
       page_scale_factor_(1.f),
+      is_pinch_gesture_active_(false),
       waiting_for_screen_rects_ack_(false),
       is_unresponsive_(false),
       in_flight_event_count_(0),
@@ -898,6 +899,7 @@
   visual_properties->max_size_for_auto_resize = max_size_for_auto_resize_;
 
   visual_properties->page_scale_factor = page_scale_factor_;
+  visual_properties->is_pinch_gesture_active = is_pinch_gesture_active_;
 
   if (view_) {
     visual_properties->new_size = view_->GetRequestedRendererSize();
@@ -984,7 +986,9 @@
       old_visual_properties_->capture_sequence_number !=
           visual_properties->capture_sequence_number ||
       old_visual_properties_->page_scale_factor !=
-          visual_properties->page_scale_factor;
+          visual_properties->page_scale_factor ||
+      old_visual_properties_->is_pinch_gesture_active !=
+          visual_properties->is_pinch_gesture_active;
 
   // We should throttle sending updated VisualProperties to the renderer to
   // the rate of commit. This ensures we don't overwhelm the renderer with
@@ -2165,8 +2169,10 @@
   max_size_for_auto_resize_ = max_size;
 }
 
-void RenderWidgetHostImpl::SetPageScaleFactor(float page_scale_factor) {
+void RenderWidgetHostImpl::SetPageScaleState(float page_scale_factor,
+                                             bool is_pinch_gesture_active) {
   page_scale_factor_ = page_scale_factor;
+  is_pinch_gesture_active_ = is_pinch_gesture_active;
 }
 
 void RenderWidgetHostImpl::Destroy(bool also_delete) {
diff --git a/content/browser/renderer_host/render_widget_host_impl.h b/content/browser/renderer_host/render_widget_host_impl.h
index 5264464..cb0b14ec 100644
--- a/content/browser/renderer_host/render_widget_host_impl.h
+++ b/content/browser/renderer_host/render_widget_host_impl.h
@@ -562,8 +562,8 @@
                      const gfx::Size& min_size,
                      const gfx::Size& max_size);
 
-  // Allows the main frame's page scale factor to be tracked.
-  void SetPageScaleFactor(float page_scale_factor);
+  // Allows the main frame's page scale state to be tracked.
+  void SetPageScaleState(float page_scale_factor, bool is_pinch_gesture_active);
 
   // Fills in the |visual_properties| struct.
   // Returns |false| if the update is redundant, |true| otherwise.
@@ -789,6 +789,8 @@
   FRIEND_TEST_ALL_PREFIXES(RenderWidgetHostViewAuraTest, AutoResizeWithScale);
   FRIEND_TEST_ALL_PREFIXES(RenderWidgetHostViewAuraTest,
                            AutoResizeWithBrowserInitiatedResize);
+  FRIEND_TEST_ALL_PREFIXES(RenderWidgetHostViewAuraTest,
+                           ChildAllocationAcceptedInParentWhileHidden);
   FRIEND_TEST_ALL_PREFIXES(RenderWidgetHostViewAuraTest, Resize);
   FRIEND_TEST_ALL_PREFIXES(RenderWidgetHostViewChildFrameTest,
                            ChildFrameAutoResizeUpdate);
@@ -1018,6 +1020,8 @@
 
   // The page-scale factor of the main-frame.
   float page_scale_factor_;
+  // True when the renderer is currently undergoing a pinch-zoom gesture.
+  bool is_pinch_gesture_active_;
 
   bool waiting_for_screen_rects_ack_;
   gfx::Rect last_view_screen_rect_;
diff --git a/content/browser/renderer_host/render_widget_host_view_aura.cc b/content/browser/renderer_host/render_widget_host_view_aura.cc
index 5e1a20e..edba25d 100644
--- a/content/browser/renderer_host/render_widget_host_view_aura.cc
+++ b/content/browser/renderer_host/render_widget_host_view_aura.cc
@@ -2214,8 +2214,19 @@
         host(), metadata.top_controls_shown_ratio);
   }
 
-  SynchronizeVisualProperties(cc::DeadlinePolicy::UseDefaultDeadline(),
-                              metadata.local_surface_id_allocation);
+  if (host()->is_hidden()) {
+    // When an embedded child responds, we want to accept its changes to the
+    // viz::LocalSurfaceId. However we do not want to embed surfaces while
+    // hidden. Nor do we want to embed invalid ids when we are evicted. Becoming
+    // visible will generate a new id, if necessary, and begin embedding.
+    // TODO(ejoe): Change the LocalSurfaceIdAllocation so that it can pause the
+    // elpased time for surface embedding. crbug/949967.
+    window_->UpdateLocalSurfaceIdFromEmbeddedClient(
+        metadata.local_surface_id_allocation);
+  } else {
+    SynchronizeVisualProperties(cc::DeadlinePolicy::UseDefaultDeadline(),
+                                metadata.local_surface_id_allocation);
+  }
 }
 
 ui::InputMethod* RenderWidgetHostViewAura::GetInputMethod() const {
diff --git a/content/browser/renderer_host/render_widget_host_view_aura_unittest.cc b/content/browser/renderer_host/render_widget_host_view_aura_unittest.cc
index 067e2c7..5895441 100644
--- a/content/browser/renderer_host/render_widget_host_view_aura_unittest.cc
+++ b/content/browser/renderer_host/render_widget_host_view_aura_unittest.cc
@@ -2831,6 +2831,46 @@
   EXPECT_EQ(local_surface_id_allocation2, local_surface_id_allocation3);
 }
 
+// This test verifies that if the parent is hidden when the child sends a
+// child-allocated viz::LocalSurfaceId, the parent will store it and it will
+// not send a WidgetMsg_SynchronizeVisualProperties back to the child.
+TEST_F(RenderWidgetHostViewAuraTest,
+       ChildAllocationAcceptedInParentWhileHidden) {
+  view_->InitAsChild(nullptr);
+  aura::client::ParentWindowWithContext(
+      view_->GetNativeView(), parent_view_->GetNativeView()->GetRootWindow(),
+      gfx::Rect());
+  sink_->ClearMessages();
+  viz::LocalSurfaceIdAllocation local_surface_id_allocation1(
+      view_->GetLocalSurfaceIdAllocation());
+  EXPECT_TRUE(local_surface_id_allocation1.IsValid());
+
+  widget_host_->SetAutoResize(true, gfx::Size(50, 50), gfx::Size(100, 100));
+  viz::ChildLocalSurfaceIdAllocator child_allocator;
+  child_allocator.UpdateFromParent(local_surface_id_allocation1);
+  child_allocator.GenerateId();
+  viz::LocalSurfaceIdAllocation local_surface_id_allocation2 =
+      child_allocator.GetCurrentLocalSurfaceIdAllocation();
+
+  view_->WasOccluded();
+  EXPECT_TRUE(widget_host_->is_hidden());
+
+  {
+    cc::RenderFrameMetadata metadata;
+    metadata.viewport_size_in_pixels = gfx::Size(75, 75);
+    metadata.local_surface_id_allocation = local_surface_id_allocation2;
+    widget_host_->DidUpdateVisualProperties(metadata);
+  }
+
+  viz::LocalSurfaceIdAllocation local_surface_id_allocation3(
+      view_->GetLocalSurfaceIdAllocation());
+  EXPECT_NE(local_surface_id_allocation1, local_surface_id_allocation3);
+  EXPECT_EQ(local_surface_id_allocation2, local_surface_id_allocation3);
+
+  EXPECT_FALSE(sink_->GetUniqueMessageMatching(
+      WidgetMsg_SynchronizeVisualProperties::ID));
+}
+
 // This test verifies that when the child and parent both allocate their own
 // viz::LocalSurfaceId the resulting conflict is resolved.
 TEST_F(RenderWidgetHostViewAuraTest, ConflictingAllocationsResolve) {
diff --git a/content/browser/site_per_process_browsertest.cc b/content/browser/site_per_process_browsertest.cc
index e870f17..bbc5c8d 100644
--- a/content/browser/site_per_process_browsertest.cc
+++ b/content/browser/site_per_process_browsertest.cc
@@ -10880,6 +10880,17 @@
   RenderFrameSubmissionObserver observer_c(child_c);
   RenderFrameSubmissionObserver observer_d(child_d);
 
+  // Monitor visual sync messages coming from the mainframe to make sure
+  // |is_pinch_gesture_active| goes true during the pinch gesture.
+  scoped_refptr<SynchronizeVisualPropertiesMessageFilter> filter_mainframe =
+      new SynchronizeVisualPropertiesMessageFilter();
+  root->current_frame_host()->GetProcess()->AddFilter(filter_mainframe.get());
+  // Monitor frame sync messages coming from child_b as it will need to
+  // relay them to child_d.
+  scoped_refptr<SynchronizeVisualPropertiesMessageFilter> filter_child_b =
+      new SynchronizeVisualPropertiesMessageFilter();
+  child_b->current_frame_host()->GetProcess()->AddFilter(filter_child_b.get());
+
   // We need to observe a root frame submission to pick up the initial page
   // scale factor.
   observer_a.WaitForAnyFrameSubmission();
@@ -10923,6 +10934,17 @@
   observer_b.WaitForExternalPageScaleFactor(target_page_scale, kScaleTolerance);
   observer_c.WaitForExternalPageScaleFactor(target_page_scale, kScaleTolerance);
   observer_d.WaitForExternalPageScaleFactor(target_page_scale, kScaleTolerance);
+
+  // The change in |is_pinch_gesture_active| that signals the end of the pinch
+  // gesture will occur sometime after the ack for GesturePinchEnd, so we need
+  // to wait for it from each renderer. If it's never seen, the test fails by
+  // timing out.
+  filter_mainframe->WaitForPinchGestureEnd();
+  EXPECT_TRUE(filter_mainframe->pinch_gesture_active_set());
+  EXPECT_TRUE(filter_mainframe->pinch_gesture_active_cleared());
+  filter_child_b->WaitForPinchGestureEnd();
+  EXPECT_TRUE(filter_child_b->pinch_gesture_active_set());
+  EXPECT_TRUE(filter_child_b->pinch_gesture_active_cleared());
 }
 
 // Verify that sandbox flags specified by a CSP header are properly inherited by
diff --git a/content/browser/webrtc/OWNERS b/content/browser/webrtc/OWNERS
index 4002051..106f113a 100644
--- a/content/browser/webrtc/OWNERS
+++ b/content/browser/webrtc/OWNERS
@@ -1,9 +1,12 @@
 chfremer@chromium.org
 emircan@chromium.org
 guidou@chromium.org
-mcasas@chromium.org
 tommi@chromium.org
 
 per-file *test*=phoglund@chromium.org
 
+# Original (legacy) owner.
+mcasas@chromium.org
+
 # COMPONENT: Blink>WebRTC
+# TEAM: webrtc-dev@chromium.org
diff --git a/content/common/frame_messages.h b/content/common/frame_messages.h
index 4784e99..48b2fe1 100644
--- a/content/common/frame_messages.h
+++ b/content/common/frame_messages.h
@@ -265,6 +265,7 @@
   IPC_STRUCT_TRAITS_MEMBER(capture_sequence_number)
   IPC_STRUCT_TRAITS_MEMBER(zoom_level)
   IPC_STRUCT_TRAITS_MEMBER(page_scale_factor)
+  IPC_STRUCT_TRAITS_MEMBER(is_pinch_gesture_active)
   IPC_STRUCT_TRAITS_MEMBER(local_surface_id_allocation)
 IPC_STRUCT_TRAITS_END()
 
diff --git a/content/common/frame_visual_properties.h b/content/common/frame_visual_properties.h
index b93180d..c897b9b 100644
--- a/content/common/frame_visual_properties.h
+++ b/content/common/frame_visual_properties.h
@@ -44,7 +44,10 @@
   // (0 is the default value which results in 1.0 zoom factor.)
   double zoom_level = 0;
 
+  // Tracks the page-scale factor and whether the frame is currently in an
+  // active pinch-zoom gesture.
   float page_scale_factor = 1.f;
+  bool is_pinch_gesture_active = false;
 
   // The time at which the viz::LocalSurfaceId used to submit this was
   // allocated.
diff --git a/content/common/visual_properties.h b/content/common/visual_properties.h
index ed6f914..dcccf0b 100644
--- a/content/common/visual_properties.h
+++ b/content/common/visual_properties.h
@@ -83,6 +83,10 @@
   // This represents the page's scale factor, which changes during pinch zoom.
   // It needs to be shared with subframes.
   float page_scale_factor = 1.f;
+
+  // Indicates whether a pinch gesture is currently active. Originates in the
+  // main frame's renderer, and needs to be shared with subframes.
+  bool is_pinch_gesture_active = false;
 };
 
 }  // namespace content
diff --git a/content/common/widget_messages.h b/content/common/widget_messages.h
index 84f6662a..fcda7ced 100644
--- a/content/common/widget_messages.h
+++ b/content/common/widget_messages.h
@@ -63,6 +63,7 @@
   IPC_STRUCT_TRAITS_MEMBER(capture_sequence_number)
   IPC_STRUCT_TRAITS_MEMBER(zoom_level)
   IPC_STRUCT_TRAITS_MEMBER(page_scale_factor)
+  IPC_STRUCT_TRAITS_MEMBER(is_pinch_gesture_active)
 IPC_STRUCT_TRAITS_END()
 
 // Traits for WebDeviceEmulationParams.
diff --git a/content/public/android/java/src/org/chromium/content/app/ContentChildProcessServiceDelegate.java b/content/public/android/java/src/org/chromium/content/app/ContentChildProcessServiceDelegate.java
index 08bd28d..b34a26de 100644
--- a/content/public/android/java/src/org/chromium/content/app/ContentChildProcessServiceDelegate.java
+++ b/content/public/android/java/src/org/chromium/content/app/ContentChildProcessServiceDelegate.java
@@ -12,7 +12,6 @@
 import android.util.SparseArray;
 import android.view.Surface;
 
-import org.chromium.base.CommandLine;
 import org.chromium.base.JNIUtils;
 import org.chromium.base.Log;
 import org.chromium.base.UnguessableToken;
@@ -31,7 +30,6 @@
 import org.chromium.content.common.SurfaceWrapper;
 import org.chromium.content_public.browser.UiThreadTaskTraits;
 import org.chromium.content_public.common.ContentProcessInfo;
-import org.chromium.content_public.common.ContentSwitches;
 
 import java.util.List;
 
@@ -83,7 +81,7 @@
         mCpuFeatures = connectionBundle.getLong(ContentChildProcessConstants.EXTRA_CPU_FEATURES);
         assert mCpuCount > 0;
 
-        if (LibraryLoader.useCrazyLinker()) {
+        if (LibraryLoader.useCrazyLinker() && !LibraryLoader.getInstance().isLoadedByZygote()) {
             Bundle sharedRelros = connectionBundle.getBundle(Linker.EXTRA_LINKER_SHARED_RELROS);
             if (sharedRelros != null) {
                 getLinker().useSharedRelros(sharedRelros);
@@ -101,13 +99,12 @@
 
     @Override
     public boolean loadNativeLibrary(Context hostContext) {
-        String processType =
-                CommandLine.getInstance().getSwitchValue(ContentSwitches.SWITCH_PROCESS_TYPE);
-        // Enable selective JNI registration when the process is not the browser process.
-        if (processType != null) {
-            JNIUtils.enableSelectiveJniRegistration();
+        if (LibraryLoader.getInstance().isLoadedByZygote()) {
+            return initializeLibrary();
         }
 
+        JNIUtils.enableSelectiveJniRegistration();
+
         Linker linker = null;
         boolean requestedSharedRelro = false;
         if (LibraryLoader.useCrazyLinker()) {
@@ -149,6 +146,11 @@
         }
         LibraryLoader.getInstance().registerRendererProcessHistogram(
                 requestedSharedRelro, loadAtFixedAddressFailed);
+
+        return initializeLibrary();
+    }
+
+    private boolean initializeLibrary() {
         try {
             LibraryLoader.getInstance().initialize(mLibraryProcessType);
         } catch (ProcessInitException e) {
diff --git a/content/public/browser/gpu_utils.cc b/content/public/browser/gpu_utils.cc
index c4b61f9..8502b82 100644
--- a/content/public/browser/gpu_utils.cc
+++ b/content/public/browser/gpu_utils.cc
@@ -115,6 +115,9 @@
   gpu_preferences.enable_vulkan =
       command_line->HasSwitch(switches::kEnableVulkan);
 
+  gpu_preferences.disable_vulkan_fallback_to_gl_for_testing =
+      command_line->HasSwitch(switches::kDisableVulkanFallbackToGLForTesting);
+
   gpu_preferences.enable_gpu_benchmarking_extension =
       command_line->HasSwitch(cc::switches::kEnableGpuBenchmarking);
 
diff --git a/content/public/test/browser_test_utils.cc b/content/public/test/browser_test_utils.cc
index 74a2c98..fc8424e 100644
--- a/content/public/test/browser_test_utils.cc
+++ b/content/public/test/browser_test_utils.cc
@@ -3110,7 +3110,10 @@
     : content::BrowserMessageFilter(kMessageClassesToFilter,
                                     base::size(kMessageClassesToFilter)),
       screen_space_rect_run_loop_(std::make_unique<base::RunLoop>()),
-      screen_space_rect_received_(false) {}
+      screen_space_rect_received_(false),
+      pinch_gesture_active_set_(false),
+      pinch_gesture_active_cleared_(false),
+      last_pinch_gesture_active_(false) {}
 
 void SynchronizeVisualPropertiesMessageFilter::WaitForRect() {
   screen_space_rect_run_loop_->Run();
@@ -3155,6 +3158,20 @@
 void SynchronizeVisualPropertiesMessageFilter::OnSynchronizeVisualProperties(
     const viz::FrameSinkId& frame_sink_id,
     const FrameVisualProperties& visual_properties) {
+  // Monitor |is_pinch_gesture_active| to determine when pinch gestures begin
+  // and end.
+  if (visual_properties.is_pinch_gesture_active &&
+      !last_pinch_gesture_active_) {
+    pinch_gesture_active_set_ = true;
+  }
+  if (!visual_properties.is_pinch_gesture_active &&
+      last_pinch_gesture_active_) {
+    pinch_gesture_active_cleared_ = true;
+    if (pinch_end_run_loop_)
+      pinch_end_run_loop_->Quit();
+  }
+  last_pinch_gesture_active_ = visual_properties.is_pinch_gesture_active;
+
   gfx::Rect screen_space_rect_in_dip = visual_properties.screen_space_rect;
   if (IsUseZoomForDSFEnabled()) {
     screen_space_rect_in_dip =
@@ -3236,6 +3253,14 @@
   return false;
 }
 
+void SynchronizeVisualPropertiesMessageFilter::WaitForPinchGestureEnd() {
+  if (pinch_gesture_active_cleared_)
+    return;
+  DCHECK(!pinch_end_run_loop_);
+  pinch_end_run_loop_ = std::make_unique<base::RunLoop>();
+  pinch_end_run_loop_->Run();
+}
+
 RenderWidgetHostMouseEventMonitor::RenderWidgetHostMouseEventMonitor(
     RenderWidgetHost* host)
     : host_(host), event_received_(false) {
diff --git a/content/public/test/browser_test_utils.h b/content/public/test/browser_test_utils.h
index 7c11f9c..69c48d8 100644
--- a/content/public/test/browser_test_utils.h
+++ b/content/public/test/browser_test_utils.h
@@ -1574,6 +1574,7 @@
 // BrowserPluginHostMsg_SynchronizeVisualProperties messages. This allows the
 // message to continue to the target child so that processing can be verified by
 // tests.
+// It also monitors for GesturePinchBegin/End events.
 class SynchronizeVisualPropertiesMessageFilter
     : public content::BrowserMessageFilter {
  public:
@@ -1592,6 +1593,11 @@
   // Waits for the next viz::LocalSurfaceId be received and returns it.
   viz::LocalSurfaceId WaitForSurfaceId();
 
+  bool pinch_gesture_active_set() { return pinch_gesture_active_set_; }
+  bool pinch_gesture_active_cleared() { return pinch_gesture_active_cleared_; }
+
+  void WaitForPinchGestureEnd();
+
  protected:
   ~SynchronizeVisualPropertiesMessageFilter() override;
 
@@ -1623,6 +1629,11 @@
   viz::LocalSurfaceId last_surface_id_;
   std::unique_ptr<base::RunLoop> surface_id_run_loop_;
 
+  bool pinch_gesture_active_set_;
+  bool pinch_gesture_active_cleared_;
+  bool last_pinch_gesture_active_;
+  std::unique_ptr<base::RunLoop> pinch_end_run_loop_;
+
   DISALLOW_COPY_AND_ASSIGN(SynchronizeVisualPropertiesMessageFilter);
 };
 
diff --git a/content/renderer/BUILD.gn b/content/renderer/BUILD.gn
index 56819fa5..9512126 100644
--- a/content/renderer/BUILD.gn
+++ b/content/renderer/BUILD.gn
@@ -73,9 +73,6 @@
     "crash_helpers.h",
     "cursor_utils.cc",
     "cursor_utils.h",
-    "devtools/render_widget_screen_metrics_emulator.cc",
-    "devtools/render_widget_screen_metrics_emulator.h",
-    "devtools/render_widget_screen_metrics_emulator_delegate.h",
     "dom_automation_controller.cc",
     "dom_automation_controller.h",
     "dom_storage/dom_storage_cached_area.cc",
@@ -468,6 +465,9 @@
     "render_widget_delegate.h",
     "render_widget_mouse_lock_dispatcher.cc",
     "render_widget_mouse_lock_dispatcher.h",
+    "render_widget_screen_metrics_emulator.cc",
+    "render_widget_screen_metrics_emulator.h",
+    "render_widget_screen_metrics_emulator_delegate.h",
     "renderer_blink_platform_impl.cc",
     "renderer_blink_platform_impl.h",
     "renderer_main.cc",
diff --git a/content/renderer/child_frame_compositing_helper.cc b/content/renderer/child_frame_compositing_helper.cc
index fda424e7..9c016e3 100644
--- a/content/renderer/child_frame_compositing_helper.cc
+++ b/content/renderer/child_frame_compositing_helper.cc
@@ -78,8 +78,10 @@
 
 void ChildFrameCompositingHelper::UpdateVisibility(bool visible) {
   cc::Layer* layer = child_frame_compositor_->GetLayer();
-  if (layer)
+  if (layer) {
     layer->SetIsDrawable(visible);
+    layer->SetHitTestable(visible);
+  }
 }
 
 gfx::Rect ChildFrameCompositingHelper::PaintableRegion() {
diff --git a/content/renderer/compositor/layer_tree_view.cc b/content/renderer/compositor/layer_tree_view.cc
index 5f163f8..6d7e1f34 100644
--- a/content/renderer/compositor/layer_tree_view.cc
+++ b/content/renderer/compositor/layer_tree_view.cc
@@ -425,8 +425,11 @@
   layer_tree_host_->SetRasterColorSpace(color_space);
 }
 
-void LayerTreeView::SetExternalPageScaleFactor(float page_scale_factor) {
-  layer_tree_host_->SetExternalPageScaleFactor(page_scale_factor);
+void LayerTreeView::SetExternalPageScaleFactor(
+    float page_scale_factor,
+    bool is_external_pinch_gesture_active) {
+  layer_tree_host_->SetExternalPageScaleFactor(
+      page_scale_factor, is_external_pinch_gesture_active);
 }
 
 void LayerTreeView::ClearCachesOnNextCommit() {
diff --git a/content/renderer/compositor/layer_tree_view.h b/content/renderer/compositor/layer_tree_view.h
index 7b0aac53..a477847 100644
--- a/content/renderer/compositor/layer_tree_view.h
+++ b/content/renderer/compositor/layer_tree_view.h
@@ -107,7 +107,8 @@
   bool SendMessageToMicroBenchmark(int id, std::unique_ptr<base::Value> value);
   void SetFrameSinkId(const viz::FrameSinkId& frame_sink_id);
   void SetRasterColorSpace(const gfx::ColorSpace& color_space);
-  void SetExternalPageScaleFactor(float page_scale_factor);
+  void SetExternalPageScaleFactor(float page_scale_factor,
+                                  bool is_external_pinch_gesture_active);
   void ClearCachesOnNextCommit();
   void SetContentSourceId(uint32_t source_id);
   void SetViewportSizeAndScale(
diff --git a/content/renderer/devtools/OWNERS b/content/renderer/devtools/OWNERS
deleted file mode 100644
index a0d7d1d59..0000000
--- a/content/renderer/devtools/OWNERS
+++ /dev/null
@@ -1,4 +0,0 @@
-alph@chromium.org
-dgozman@chromium.org
-
-# COMPONENT: Platform>DevTools
diff --git a/content/renderer/image_capture/OWNERS b/content/renderer/image_capture/OWNERS
index 57aa41f..ab8e7ba 100644
--- a/content/renderer/image_capture/OWNERS
+++ b/content/renderer/image_capture/OWNERS
@@ -1,5 +1,7 @@
-mcasas@chromium.org
 reillyg@chromium.org
 
+# Original (legacy) owner.
+mcasas@chromium.org
+
 # COMPONENT: Blink>ImageCapture
-# TEAM: media-dev@chromium.org
+# TEAM: webrtc-dev@chromium.org
diff --git a/content/renderer/input/render_widget_input_handler.cc b/content/renderer/input/render_widget_input_handler.cc
index 0c76af8..e192902 100644
--- a/content/renderer/input/render_widget_input_handler.cc
+++ b/content/renderer/input/render_widget_input_handler.cc
@@ -192,8 +192,11 @@
 viz::FrameSinkId GetRemoteFrameSinkId(const blink::WebNode& node) {
   blink::WebFrame* result_frame = blink::WebFrame::FromFrameOwnerElement(node);
   if (result_frame && result_frame->IsWebRemoteFrame()) {
-    return RenderFrameProxy::FromWebFrame(result_frame->ToWebRemoteFrame())
-        ->frame_sink_id();
+    blink::WebRemoteFrame* remote_frame = result_frame->ToWebRemoteFrame();
+    if (remote_frame->IsIgnoredForHitTest())
+      return viz::FrameSinkId();
+
+    return RenderFrameProxy::FromWebFrame(remote_frame)->frame_sink_id();
   }
   auto* plugin = BrowserPlugin::GetFromNode(node);
   return plugin ? plugin->frame_sink_id() : viz::FrameSinkId();
diff --git a/content/renderer/media/stream/OWNERS b/content/renderer/media/stream/OWNERS
index c205d4f9..32889cc 100644
--- a/content/renderer/media/stream/OWNERS
+++ b/content/renderer/media/stream/OWNERS
@@ -2,5 +2,5 @@
 
 per-file media_stream_audio_processor*=aluebs@chromium.org
 
-# TEAM: media-capture-and-streams@grotations.appspotmail.com
+# TEAM: webrtc-dev@chromium.org
 # COMPONENT: Blink>GetUserMedia
diff --git a/content/renderer/media/webrtc/mock_data_channel_impl.cc b/content/renderer/media/webrtc/mock_data_channel_impl.cc
index 5cdf70cc..6f4e886c1 100644
--- a/content/renderer/media/webrtc/mock_data_channel_impl.cc
+++ b/content/renderer/media/webrtc/mock_data_channel_impl.cc
@@ -35,11 +35,15 @@
 bool MockDataChannel::ordered() const { return config_.ordered; }
 
 uint16_t MockDataChannel::maxRetransmitTime() const {
-  return config_.maxRetransmitTime;
+  // TODO(https://bugs.chromium.org/854385): Restore when change landed.
+  // return config_.maxRetransmitTime;
+  return -1;
 }
 
 uint16_t MockDataChannel::maxRetransmits() const {
-  return config_.maxRetransmits;
+  // TODO(https://bugs.chromium.org/854385): Restore when change landed.
+  // return config_.maxRetransmits;
+  return -1;
 }
 
 std::string MockDataChannel::protocol() const { return config_.protocol; }
diff --git a/content/renderer/media_capture_from_element/OWNERS b/content/renderer/media_capture_from_element/OWNERS
index 446f8fb0..d176cccc 100644
--- a/content/renderer/media_capture_from_element/OWNERS
+++ b/content/renderer/media_capture_from_element/OWNERS
@@ -1,4 +1,7 @@
 emircan@chromium.org
+
+# Original (legacy) owner.
 mcasas@chromium.org
 
 # COMPONENT: Blink>MediaStream>CaptureFromElement
+# TEAM: webrtc-dev@chromium.org
diff --git a/content/renderer/media_recorder/OWNERS b/content/renderer/media_recorder/OWNERS
index 6675929..c6eb0c4 100644
--- a/content/renderer/media_recorder/OWNERS
+++ b/content/renderer/media_recorder/OWNERS
@@ -1,5 +1,7 @@
 emircan@chromium.org
+
+# Original (legacy) owner.
 mcasas@chromium.org
 
 # COMPONENT: Blink>MediaRecording
-# TEAM: media-dev@chromium.org
+# TEAM: webrtc-dev@chromium.org
diff --git a/content/renderer/render_frame_impl.cc b/content/renderer/render_frame_impl.cc
index 75479724..c6418073 100644
--- a/content/renderer/render_frame_impl.cc
+++ b/content/renderer/render_frame_impl.cc
@@ -1454,11 +1454,10 @@
         replicated_state.unique_name);
     web_frame = parent_web_frame->CreateLocalChild(
         replicated_state.scope, WebString::FromUTF8(replicated_state.name),
-        replicated_state.frame_policy.sandbox_flags, render_frame,
+        replicated_state.frame_policy, render_frame,
         render_frame->blink_interface_registry_.get(),
         document_interface_broker_blink.PassInterface().PassHandle(),
         previous_sibling_web_frame,
-        replicated_state.frame_policy.container_policy,
         ConvertFrameOwnerPropertiesToWebFrameOwnerProperties(
             frame_owner_properties),
         replicated_state.frame_owner_element_type,
@@ -1494,8 +1493,7 @@
     web_frame = blink::WebLocalFrame::CreateProvisional(
         render_frame, render_frame->blink_interface_registry_.get(),
         document_interface_broker_blink.PassInterface().PassHandle(),
-        proxy->web_frame(), replicated_state.frame_policy.sandbox_flags,
-        replicated_state.frame_policy.container_policy);
+        proxy->web_frame(), replicated_state.frame_policy);
     // The new |web_frame| is a main frame iff the proxy's frame was.
     DCHECK_EQ(proxy_is_main_frame, !web_frame->Parent());
   }
@@ -2697,8 +2695,7 @@
 
 void RenderFrameImpl::OnDidUpdateFramePolicy(
     const blink::FramePolicy& frame_policy) {
-  frame_->SetFrameOwnerPolicy(frame_policy.sandbox_flags,
-                              frame_policy.container_policy);
+  frame_->SetFrameOwnerPolicy(frame_policy);
 }
 
 void RenderFrameImpl::OnSetFrameOwnerProperties(
@@ -4193,8 +4190,7 @@
     blink::WebTreeScopeType scope,
     const blink::WebString& name,
     const blink::WebString& fallback_name,
-    blink::WebSandboxFlags sandbox_flags,
-    const blink::ParsedFeaturePolicy& container_policy,
+    const blink::FramePolicy& frame_policy,
     const blink::WebFrameOwnerProperties& frame_owner_properties,
     blink::FrameOwnerElementType frame_owner_element_type) {
   DCHECK_EQ(frame_, parent);
@@ -4227,7 +4223,7 @@
   params.frame_unique_name = unique_name_helper_.GenerateNameForNewChildFrame(
       params.frame_name.empty() ? fallback_name.Utf8() : params.frame_name,
       params.is_created_by_script);
-  params.frame_policy = {sandbox_flags, container_policy};
+  params.frame_policy = frame_policy;
   params.frame_owner_properties =
       ConvertWebFrameOwnerPropertiesToFrameOwnerProperties(
           frame_owner_properties);
@@ -4437,11 +4433,10 @@
 
 void RenderFrameImpl::DidChangeFramePolicy(
     blink::WebFrame* child_frame,
-    blink::WebSandboxFlags flags,
-    const blink::ParsedFeaturePolicy& container_policy) {
+    const blink::FramePolicy& frame_policy) {
   Send(new FrameHostMsg_DidChangeFramePolicy(
       routing_id_, RenderFrame::GetRoutingIdForWebFrame(child_frame),
-      {flags, container_policy}));
+      frame_policy));
 }
 
 void RenderFrameImpl::DidSetFramePolicyHeaders(
diff --git a/content/renderer/render_frame_impl.h b/content/renderer/render_frame_impl.h
index 1f6e37c..a12dfe6 100644
--- a/content/renderer/render_frame_impl.h
+++ b/content/renderer/render_frame_impl.h
@@ -695,8 +695,7 @@
       blink::WebTreeScopeType scope,
       const blink::WebString& name,
       const blink::WebString& fallback_name,
-      blink::WebSandboxFlags sandbox_flags,
-      const blink::ParsedFeaturePolicy& container_policy,
+      const blink::FramePolicy& frame_policy,
       const blink::WebFrameOwnerProperties& frame_owner_properties,
       blink::FrameOwnerElementType frame_owner_element_type) override;
   std::pair<blink::WebRemoteFrame*, base::UnguessableToken> CreatePortal(
@@ -712,10 +711,8 @@
       blink::WebInsecureRequestPolicy policy) override;
   void DidEnforceInsecureNavigationsSet(
       const std::vector<uint32_t>& set) override;
-  void DidChangeFramePolicy(
-      blink::WebFrame* child_frame,
-      blink::WebSandboxFlags flags,
-      const blink::ParsedFeaturePolicy& container_policy) override;
+  void DidChangeFramePolicy(blink::WebFrame* child_frame,
+                            const blink::FramePolicy& frame_policy) override;
   void DidSetFramePolicyHeaders(
       blink::WebSandboxFlags flags,
       const blink::ParsedFeaturePolicy& parsed_header) override;
diff --git a/content/renderer/render_frame_proxy.cc b/content/renderer/render_frame_proxy.cc
index d4e8c3f0..ea8f000 100644
--- a/content/renderer/render_frame_proxy.cc
+++ b/content/renderer/render_frame_proxy.cc
@@ -154,8 +154,7 @@
     web_frame = parent->web_frame()->CreateRemoteChild(
         replicated_state.scope,
         blink::WebString::FromUTF8(replicated_state.name),
-        replicated_state.frame_policy.sandbox_flags,
-        replicated_state.frame_policy.container_policy,
+        replicated_state.frame_policy,
         replicated_state.frame_owner_element_type, proxy.get(), opener);
     proxy->unique_name_ = replicated_state.unique_name;
     render_view = parent->render_view();
@@ -311,8 +310,10 @@
   SynchronizeVisualProperties();
 }
 
-void RenderFrameProxy::OnPageScaleFactorChanged(float page_scale_factor) {
+void RenderFrameProxy::OnPageScaleFactorChanged(float page_scale_factor,
+                                                bool is_pinch_gesture_active) {
   pending_visual_properties_.page_scale_factor = page_scale_factor;
+  pending_visual_properties_.is_pinch_gesture_active = is_pinch_gesture_active;
   SynchronizeVisualProperties();
 }
 
@@ -382,8 +383,7 @@
 void RenderFrameProxy::OnDidUpdateFramePolicy(
     const blink::FramePolicy& frame_policy) {
   DCHECK(web_frame()->Parent());
-  web_frame_->SetFrameOwnerPolicy(frame_policy.sandbox_flags,
-                                  frame_policy.container_policy);
+  web_frame_->SetFrameOwnerPolicy(frame_policy);
 }
 
 // Update the proxy's SecurityContext with new sandbox flags or feature policy
@@ -687,6 +687,8 @@
           pending_visual_properties_.zoom_level ||
       sent_visual_properties_->page_scale_factor !=
           pending_visual_properties_.page_scale_factor ||
+      sent_visual_properties_->is_pinch_gesture_active !=
+          pending_visual_properties_.is_pinch_gesture_active ||
       capture_sequence_number_changed;
 
   if (synchronized_props_changed) {
diff --git a/content/renderer/render_frame_proxy.h b/content/renderer/render_frame_proxy.h
index 947c70a..bebf7d1 100644
--- a/content/renderer/render_frame_proxy.h
+++ b/content/renderer/render_frame_proxy.h
@@ -145,8 +145,9 @@
   void OnZoomLevelChanged(double zoom_level);
 
   // Out-of-process child frames receive a signal from RenderWidget when the
-  // page scale factor has changed.
-  void OnPageScaleFactorChanged(float page_scale_factor);
+  // page scale factor has changed, and/or a pinch-zoom gesture starts/ends.
+  void OnPageScaleFactorChanged(float page_scale_factor,
+                                bool is_pinch_gesture_active);
 
   // Invoked by RenderWidget when a new capture sequence number was set,
   // indicating that surfaces should be synchronized.
@@ -229,6 +230,10 @@
 
   void WasEvicted();
 
+  bool is_pinch_gesture_active_for_testing() {
+    return pending_visual_properties_.is_pinch_gesture_active;
+  }
+
  private:
   RenderFrameProxy(int routing_id);
 
diff --git a/content/renderer/render_view_browsertest.cc b/content/renderer/render_view_browsertest.cc
index 0ff9552..3bb88dc9 100644
--- a/content/renderer/render_view_browsertest.cc
+++ b/content/renderer/render_view_browsertest.cc
@@ -548,6 +548,54 @@
   }
 };
 
+TEST_F(RenderViewImplTest, IsPinchGestureActivePropagatesToProxies) {
+  LoadHTML(
+      "<body style='min-height:1000px;'>"
+      "  <iframe src='data:text/html,frame 1'></iframe>"
+      "  <iframe src='data:text/html,frame 2'></iframe>"
+      "</body>");
+
+  // Verify child's proxy doesn't think we're pinching.
+  blink::WebFrame* root_web_frame = frame()->GetWebFrame();
+  ASSERT_TRUE(root_web_frame->FirstChild()->IsWebLocalFrame());
+  TestRenderFrame* child_frame_1 =
+      static_cast<TestRenderFrame*>(RenderFrame::FromWebFrame(
+          root_web_frame->FirstChild()->ToWebLocalFrame()));
+  ASSERT_TRUE(child_frame_1);
+  TestRenderFrame* child_frame_2 =
+      static_cast<TestRenderFrame*>(RenderFrame::FromWebFrame(
+          root_web_frame->FirstChild()->NextSibling()->ToWebLocalFrame()));
+  ASSERT_TRUE(child_frame_2);
+  child_frame_1->SwapOut(kProxyRoutingId, true,
+                         ReconstructReplicationStateForTesting(child_frame_1));
+  EXPECT_TRUE(root_web_frame->FirstChild()->IsWebRemoteFrame());
+  RenderFrameProxy* child_proxy_1 = RenderFrameProxy::FromWebFrame(
+      root_web_frame->FirstChild()->ToWebRemoteFrame());
+  ASSERT_TRUE(child_proxy_1);
+  EXPECT_FALSE(child_proxy_1->is_pinch_gesture_active_for_testing());
+
+  // Set the |is_pinch_gesture_active| flag.
+  view()->PageScaleFactorChanged(1.f, true);
+  EXPECT_TRUE(child_proxy_1->is_pinch_gesture_active_for_testing());
+
+  // Create a new remote child, and get its proxy. Swapping out will force
+  // creation and registering of a new RenderFrameProxy, which should pick up
+  // the existing setting.
+  child_frame_2->SwapOut(kProxyRoutingId + 1, true,
+                         ReconstructReplicationStateForTesting(child_frame_2));
+  EXPECT_TRUE(root_web_frame->FirstChild()->NextSibling()->IsWebRemoteFrame());
+  RenderFrameProxy* child_proxy_2 = RenderFrameProxy::FromWebFrame(
+      root_web_frame->FirstChild()->NextSibling()->ToWebRemoteFrame());
+
+  // Verify new child has the flag too.
+  EXPECT_TRUE(child_proxy_2->is_pinch_gesture_active_for_testing());
+
+  // Reset the flag, make sure both children respond.
+  view()->PageScaleFactorChanged(1.f, false);
+  EXPECT_FALSE(child_proxy_1->is_pinch_gesture_active_for_testing());
+  EXPECT_FALSE(child_proxy_2->is_pinch_gesture_active_for_testing());
+}
+
 // Test that we get form state change notifications when input fields change.
 TEST_F(RenderViewImplTest, OnNavStateChanged) {
   view()->set_send_content_state_immediately(true);
diff --git a/content/renderer/render_widget.cc b/content/renderer/render_widget.cc
index 9fb5ae6..4d3da78 100644
--- a/content/renderer/render_widget.cc
+++ b/content/renderer/render_widget.cc
@@ -57,7 +57,6 @@
 #include "content/renderer/browser_plugin/browser_plugin.h"
 #include "content/renderer/compositor/layer_tree_view.h"
 #include "content/renderer/cursor_utils.h"
-#include "content/renderer/devtools/render_widget_screen_metrics_emulator.h"
 #include "content/renderer/drop_data_builder.h"
 #include "content/renderer/external_popup_menu.h"
 #include "content/renderer/frame_swap_message_queue.h"
@@ -72,6 +71,7 @@
 #include "content/renderer/render_process.h"
 #include "content/renderer/render_thread_impl.h"
 #include "content/renderer/render_view_impl.h"
+#include "content/renderer/render_widget_screen_metrics_emulator.h"
 #include "content/renderer/renderer_blink_platform_impl.h"
 #include "gpu/command_buffer/service/gpu_switches.h"
 #include "ipc/ipc_message_start.h"
@@ -860,10 +860,18 @@
         // the visual properties here. While blink doesn't need to know this
         // page scale factor outside the main frame, the compositor does in
         // order to produce its output at the correct scale.
-        layer_tree_view_->SetExternalPageScaleFactor(params.page_scale_factor);
+        layer_tree_view_->SetExternalPageScaleFactor(
+            params.page_scale_factor, params.is_pinch_gesture_active);
         // Store the value to give to any new RenderFrameProxy that is
         // registered.
         page_scale_factor_from_mainframe_ = params.page_scale_factor;
+        // Similarly, only the main frame knows when a pinch gesture is active,
+        // but this information is needed in subframes so they can throttle
+        // re-rastering in the same manner as the main frame.
+        // |is_pinch_gesture_active| follows the same path to the subframe
+        // compositor(s) as |page_scale_factor|.
+        is_pinch_gesture_active_from_mainframe_ =
+            params.is_pinch_gesture_active;
         // Push the page scale factor down to any child RenderWidgets via our
         // child proxy frames.
         // TODO(danakj): This ends up setting the page scale factor in the
@@ -872,8 +880,10 @@
         // global value per-page, we could instead store it once in the browser
         // (such as in RenderViewHost) and distribute it to each frame-hosted
         // RenderWidget from there.
-        for (auto& child_proxy : render_frame_proxies_)
-          child_proxy.OnPageScaleFactorChanged(params.page_scale_factor);
+        for (auto& child_proxy : render_frame_proxies_) {
+          child_proxy.OnPageScaleFactorChanged(params.page_scale_factor,
+                                               params.is_pinch_gesture_active);
+        }
       }
 
       gfx::Size old_visible_viewport_size = visible_viewport_size_;
@@ -3440,7 +3450,8 @@
   // frame trees). A new RenderFrameProxy means there is a new child
   // RenderWidget in another frame tree. In order for it to hear about
   // the page scale factor we pass along the last seen value here.
-  proxy->OnPageScaleFactorChanged(page_scale_factor_from_mainframe_);
+  proxy->OnPageScaleFactorChanged(page_scale_factor_from_mainframe_,
+                                  is_pinch_gesture_active_from_mainframe_);
 }
 
 void RenderWidget::UnregisterRenderFrameProxy(RenderFrameProxy* proxy) {
@@ -3587,21 +3598,16 @@
   // of which will be via proxy child frame). These will each in turn forward
   // the message to their child RenderWidgets (through their proxy child
   // frames).
-  // TODO(crbug.com/924336): This value is continuously propagated during a
-  // pinch-zoom, causing the child RenderWidgets to re-raster, while the main
-  // frame is able to throttle re-raster to powers of 2. We could find some way
-  // to throttle child RenderWidgets also, perhaps by informing them when the
-  // pinch-zoom gesture is started and stopped.
   DCHECK(!is_frozen_);
   DCHECK(delegate());
 
-  // TODO(wjmaclean): In the next CL, plumb |is_pinch_gesture_active| into the
-  // observer via observer.OnPageScaleFactorChanged() and allow it to trigger
-  // SynchronizeVisualProperties if needed.
-  for (auto& observer : render_frame_proxies_)
-    observer.OnPageScaleFactorChanged(page_scale_factor);
+  for (auto& observer : render_frame_proxies_) {
+    observer.OnPageScaleFactorChanged(page_scale_factor,
+                                      is_pinch_gesture_active);
+  }
   // Store the value to give to any new RenderFrameProxy that is registered.
   page_scale_factor_from_mainframe_ = page_scale_factor;
+  is_pinch_gesture_active_from_mainframe_ = is_pinch_gesture_active;
 }
 
 void RenderWidget::UseSynchronousResizeModeForTesting(bool enable) {
diff --git a/content/renderer/render_widget.h b/content/renderer/render_widget.h
index d0fd4036..34ce2a6 100644
--- a/content/renderer/render_widget.h
+++ b/content/renderer/render_widget.h
@@ -39,13 +39,13 @@
 #include "content/public/common/drop_data.h"
 #include "content/public/common/screen_info.h"
 #include "content/renderer/compositor/layer_tree_view_delegate.h"
-#include "content/renderer/devtools/render_widget_screen_metrics_emulator_delegate.h"
 #include "content/renderer/input/main_thread_event_queue.h"
 #include "content/renderer/input/render_widget_input_handler.h"
 #include "content/renderer/input/render_widget_input_handler_delegate.h"
 #include "content/renderer/mouse_lock_dispatcher.h"
 #include "content/renderer/render_widget_delegate.h"
 #include "content/renderer/render_widget_mouse_lock_dispatcher.h"
+#include "content/renderer/render_widget_screen_metrics_emulator_delegate.h"
 #include "ipc/ipc_listener.h"
 #include "ipc/ipc_message.h"
 #include "ipc/ipc_sender.h"
@@ -1136,10 +1136,11 @@
   // The height of the browser bottom controls.
   float bottom_controls_height_ = 0.f;
 
-  // The last seen page scale factor, which comes from the main frame and is
-  // propagated through the RenderWidget tree. This value is passed to any new
+  // The last seen page scale state, which comes from the main frame and is
+  // propagated through the RenderWidget tree. This state is passed to any new
   // child RenderWidget.
   float page_scale_factor_from_mainframe_ = 1.f;
+  bool is_pinch_gesture_active_from_mainframe_ = false;
 
   // This is initialized to zero and is incremented on each non-same-page
   // navigation commit by RenderFrameImpl. At that time it is sent to the
diff --git a/content/renderer/render_widget_delegate.h b/content/renderer/render_widget_delegate.h
index 4052e67..1252e15 100644
--- a/content/renderer/render_widget_delegate.h
+++ b/content/renderer/render_widget_delegate.h
@@ -11,6 +11,7 @@
 class WebMouseEvent;
 class WebWidget;
 class WebWidgetClient;
+struct WebDeviceEmulationParams;
 }  // namespace blink
 
 namespace content {
diff --git a/content/renderer/render_widget_fullscreen_pepper.cc b/content/renderer/render_widget_fullscreen_pepper.cc
index d24067e..ebcba1e 100644
--- a/content/renderer/render_widget_fullscreen_pepper.cc
+++ b/content/renderer/render_widget_fullscreen_pepper.cc
@@ -345,6 +345,7 @@
   }
   UpdateLayerBounds();
   layer_->SetIsDrawable(true);
+  layer_->SetHitTestable(true);
   layer_tree_view()->SetNonBlinkManagedRootLayer(layer_);
 }
 
diff --git a/content/renderer/devtools/render_widget_screen_metrics_emulator.cc b/content/renderer/render_widget_screen_metrics_emulator.cc
similarity index 97%
rename from content/renderer/devtools/render_widget_screen_metrics_emulator.cc
rename to content/renderer/render_widget_screen_metrics_emulator.cc
index 6c60733..29d798d 100644
--- a/content/renderer/devtools/render_widget_screen_metrics_emulator.cc
+++ b/content/renderer/render_widget_screen_metrics_emulator.cc
@@ -2,11 +2,11 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "content/renderer/devtools/render_widget_screen_metrics_emulator.h"
+#include "content/renderer/render_widget_screen_metrics_emulator.h"
 
 #include "content/common/visual_properties.h"
 #include "content/public/common/context_menu_params.h"
-#include "content/renderer/devtools/render_widget_screen_metrics_emulator_delegate.h"
+#include "content/renderer/render_widget_screen_metrics_emulator_delegate.h"
 
 namespace content {
 
diff --git a/content/renderer/devtools/render_widget_screen_metrics_emulator.h b/content/renderer/render_widget_screen_metrics_emulator.h
similarity index 92%
rename from content/renderer/devtools/render_widget_screen_metrics_emulator.h
rename to content/renderer/render_widget_screen_metrics_emulator.h
index 287ccbc2..4a98f3e 100644
--- a/content/renderer/devtools/render_widget_screen_metrics_emulator.h
+++ b/content/renderer/render_widget_screen_metrics_emulator.h
@@ -2,8 +2,8 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef CONTENT_RENDERER_DEVTOOLS_RENDER_WIDGET_SCREEN_METRICS_EMULATOR_H_
-#define CONTENT_RENDERER_DEVTOOLS_RENDER_WIDGET_SCREEN_METRICS_EMULATOR_H_
+#ifndef CONTENT_RENDERER_RENDER_WIDGET_SCREEN_METRICS_EMULATOR_H_
+#define CONTENT_RENDERER_RENDER_WIDGET_SCREEN_METRICS_EMULATOR_H_
 
 #include <memory>
 
@@ -83,4 +83,4 @@
 
 }  // namespace content
 
-#endif  // CONTENT_RENDERER_DEVTOOLS_RENDER_WIDGET_SCREEN_METRICS_EMULATOR_H_
+#endif  // CONTENT_RENDERER_RENDER_WIDGET_SCREEN_METRICS_EMULATOR_H_
diff --git a/content/renderer/devtools/render_widget_screen_metrics_emulator_delegate.h b/content/renderer/render_widget_screen_metrics_emulator_delegate.h
similarity index 82%
rename from content/renderer/devtools/render_widget_screen_metrics_emulator_delegate.h
rename to content/renderer/render_widget_screen_metrics_emulator_delegate.h
index fdaadb8..6d94586c 100644
--- a/content/renderer/devtools/render_widget_screen_metrics_emulator_delegate.h
+++ b/content/renderer/render_widget_screen_metrics_emulator_delegate.h
@@ -2,8 +2,8 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef CONTENT_RENDERER_DEVTOOLS_RENDER_WIDGET_SCREEN_METRICS_EMULATOR_DELEGATE_H_
-#define CONTENT_RENDERER_DEVTOOLS_RENDER_WIDGET_SCREEN_METRICS_EMULATOR_DELEGATE_H_
+#ifndef CONTENT_RENDERER_RENDER_WIDGET_SCREEN_METRICS_EMULATOR_DELEGATE_H_
+#define CONTENT_RENDERER_RENDER_WIDGET_SCREEN_METRICS_EMULATOR_DELEGATE_H_
 
 #include "content/common/content_export.h"
 
@@ -39,4 +39,4 @@
 
 }  // namespace content
 
-#endif  // CONTENT_RENDERER_DEVTOOLS_RENDER_WIDGET_SCREEN_METRICS_EMULATOR_DELEGATE_H_
+#endif  // CONTENT_RENDERER_RENDER_WIDGET_SCREEN_METRICS_EMULATOR_DELEGATE_H_
diff --git a/content/renderer/render_widget_unittest.cc b/content/renderer/render_widget_unittest.cc
index 023f4ede..2454bc48 100644
--- a/content/renderer/render_widget_unittest.cc
+++ b/content/renderer/render_widget_unittest.cc
@@ -15,11 +15,13 @@
 #include "base/test/metrics/histogram_tester.h"
 #include "base/test/scoped_feature_list.h"
 #include "base/test/scoped_task_environment.h"
+#include "base/unguessable_token.h"
 #include "build/build_config.h"
 #include "cc/layers/solid_color_layer.h"
 #include "cc/trees/layer_tree_host.h"
 #include "components/viz/common/features.h"
 #include "components/viz/common/surfaces/parent_local_surface_id_allocator.h"
+#include "content/common/frame_replication_state.h"
 #include "content/common/input/input_handler.mojom.h"
 #include "content/common/input/synthetic_web_input_event_builders.h"
 #include "content/common/input_messages.h"
@@ -29,9 +31,10 @@
 #include "content/public/common/content_features.h"
 #include "content/public/test/mock_render_thread.h"
 #include "content/renderer/compositor/layer_tree_view.h"
-#include "content/renderer/devtools/render_widget_screen_metrics_emulator.h"
 #include "content/renderer/input/widget_input_handler_manager.h"
+#include "content/renderer/render_frame_proxy.h"
 #include "content/renderer/render_widget_delegate.h"
+#include "content/renderer/render_widget_screen_metrics_emulator.h"
 #include "content/test/fake_compositor_dependencies.h"
 #include "content/test/mock_render_process.h"
 #include "ipc/ipc_test_sink.h"
@@ -551,6 +554,41 @@
       const blink::WebDeviceEmulationParams& params) override {}
 };
 
+// Tests that the value of VisualProperties::is_pinch_gesture_active is
+// propagated to the LayerTreeHost when properties are synced, but only for
+// subframe widgets.
+TEST_F(RenderWidgetUnittest, ActivePinchGestureUpdatesLayerTreeHost) {
+  auto* layer_tree_host = widget()->layer_tree_view()->layer_tree_host();
+  EXPECT_FALSE(layer_tree_host->is_external_pinch_gesture_active_for_testing());
+  content::VisualProperties visual_properties;
+
+  // Sync visual properties on a child RenderWidget.
+  visual_properties.is_pinch_gesture_active = true;
+  widget()->OnSynchronizeVisualProperties(visual_properties);
+  // We expect the |is_pinch_gesture_active| value to propagate to the
+  // LayerTreeHost for sub-frames. Since GesturePinch events are handled
+  // directly in the main-frame's layer tree (and only there), information about
+  // whether or not we're in a pinch gesture must be communicated separately to
+  // sub-frame layer trees, via SynchronizeVisualProperties. This information
+  // is required to allow sub-frame compositors to throttle rastering while
+  // pinch gestures are active.
+  EXPECT_TRUE(layer_tree_host->is_external_pinch_gesture_active_for_testing());
+  visual_properties.is_pinch_gesture_active = false;
+  widget()->OnSynchronizeVisualProperties(visual_properties);
+  EXPECT_FALSE(layer_tree_host->is_external_pinch_gesture_active_for_testing());
+
+  // Repeat with a 'mainframe' widget.
+  widget()->set_delegate(std::make_unique<StubRenderWidgetDelegate>());
+  visual_properties.is_pinch_gesture_active = true;
+  widget()->OnSynchronizeVisualProperties(visual_properties);
+  // We do not expect the |is_pinch_gesture_active| value to propagate to the
+  // LayerTreeHost for the main-frame. Since GesturePinch events are handled
+  // directly by the layer tree for the main frame, it already knows whether or
+  // not a pinch gesture is active, and so we shouldn't propagate this
+  // information to the layer tree for a main-frame's widget.
+  EXPECT_FALSE(layer_tree_host->is_external_pinch_gesture_active_for_testing());
+}
+
 TEST_F(RenderWidgetPopupUnittest, EmulatingPopupRect) {
   blink::WebRect popup_screen_rect(200, 250, 100, 400);
   widget()->SetWindowRect(popup_screen_rect);
diff --git a/content/shell/test_runner/web_ax_object_proxy.cc b/content/shell/test_runner/web_ax_object_proxy.cc
index 8cebe74..5298659f 100644
--- a/content/shell/test_runner/web_ax_object_proxy.cc
+++ b/content/shell/test_runner/web_ax_object_proxy.cc
@@ -742,7 +742,6 @@
                  &WebAXObjectProxy::AriaFlowToElementAtIndex)
       .SetMethod("ariaOwnsElementAtIndex",
                  &WebAXObjectProxy::AriaOwnsElementAtIndex)
-      .SetMethod("lineForIndex", &WebAXObjectProxy::LineForIndex)
       .SetMethod("boundsForRange", &WebAXObjectProxy::BoundsForRange)
       .SetMethod("childAtIndex", &WebAXObjectProxy::ChildAtIndex)
       .SetMethod("elementAtPoint", &WebAXObjectProxy::ElementAtPoint)
@@ -1486,17 +1485,6 @@
   return collector.attributes();
 }
 
-int WebAXObjectProxy::LineForIndex(int index) {
-  accessibility_object_.UpdateLayoutAndCheckValidity();
-  blink::WebVector<int> line_breaks;
-  accessibility_object_.LineBreaks(line_breaks);
-  int line = 0;
-  int vector_size = static_cast<int>(line_breaks.size());
-  while (line < vector_size && line_breaks[line] <= index)
-    line++;
-  return line;
-}
-
 std::string WebAXObjectProxy::BoundsForRange(int start, int end) {
   accessibility_object_.UpdateLayoutAndCheckValidity();
   if (accessibility_object_.Role() != ax::mojom::Role::kStaticText)
diff --git a/content/shell/test_runner/web_ax_object_proxy.h b/content/shell/test_runner/web_ax_object_proxy.h
index dfa1494..da30a01b 100644
--- a/content/shell/test_runner/web_ax_object_proxy.h
+++ b/content/shell/test_runner/web_ax_object_proxy.h
@@ -158,7 +158,6 @@
   v8::Local<v8::Object> AriaOwnsElementAtIndex(unsigned index);
   std::string AllAttributes();
   std::string AttributesOfChildren();
-  int LineForIndex(int index);
   std::string BoundsForRange(int start, int end);
   v8::Local<v8::Object> ChildAtIndex(int index);
   v8::Local<v8::Object> ElementAtPoint(int x, int y);
diff --git a/content/test/BUILD.gn b/content/test/BUILD.gn
index 3eb36ce..eb8cce8c 100644
--- a/content/test/BUILD.gn
+++ b/content/test/BUILD.gn
@@ -394,7 +394,6 @@
     "//third_party/webrtc/api:rtc_stats_api",
     "//third_party/webrtc/api:scoped_refptr",
     "//third_party/webrtc/media:rtc_media_base",
-    "//third_party/webrtc/modules/video_capture",
     "//third_party/webrtc/pc:libjingle_peerconnection",
     "//third_party/webrtc/rtc_base:rtc_base_approved",
     "//third_party/webrtc/stats:rtc_stats",
@@ -2018,7 +2017,6 @@
     "//third_party/webrtc/media:rtc_media",
     "//third_party/webrtc/media:rtc_vp9_profile",
     "//third_party/webrtc/modules/desktop_capture:primitives",
-    "//third_party/webrtc/modules/video_capture",
     "//third_party/webrtc/modules/video_coding:video_codec_interface",
     "//third_party/webrtc/pc:libjingle_peerconnection",
     "//third_party/webrtc/rtc_base:ip_address",
diff --git a/content/test/data/frame_tree/page_with_ancestor_masked_iframe.html b/content/test/data/frame_tree/page_with_ancestor_masked_iframe.html
index 6331a6f..d9faa98 100644
--- a/content/test/data/frame_tree/page_with_ancestor_masked_iframe.html
+++ b/content/test/data/frame_tree/page_with_ancestor_masked_iframe.html
@@ -20,6 +20,7 @@
       width: 200px;
       height: 200px;
       border: none;
+      opacity: 0.5;
     }
     </style>
   </head>
diff --git a/docs/gpu/gpu_testing_bot_details.md b/docs/gpu/gpu_testing_bot_details.md
index b2b5317..853cf6c 100644
--- a/docs/gpu/gpu_testing_bot_details.md
+++ b/docs/gpu/gpu_testing_bot_details.md
@@ -535,63 +535,55 @@
 [go/chromecals]: http://go/chromecals
 
 
-### How to add a new "optional" try bot
+### How to add a new try bot that runs a subset of tests or extra tests
 
-TODO(kbr): the naming of the "optional" try bots is confusing and
-unfortunate. They should probably be renamed to something like "extratests" or
-"extra_tests", so perhaps a new naming convention of "gpu_win_extratests_rel" or
-"win_gpu_extratests_rel". Unfortunately making this change at this point
-requires touching tons of files across many workspaces and is unlikely to happen
-unless someone highly motivated wants to pick up the task.
+Several projects (ANGLE, Dawn) run custom tests using the Chromium recipes. They
+use try bot bot configs that run subsets of Chromium or additional slower tests
+that can't be run on the main CQ.
 
-The "optional" GPU try bots are a concession to the reality that there are some
-long-running GPU test suites that simply can not run against every Chromium CL.
-They run some additional tests that are usually run only on the
-chromium.gpu.fyi waterfall. Some of these tests, like the WebGL 2.0 conformance
-suite, are intended to be run on the normal try bots once hardware capacity is
-available. Some are not intended to ever run on the normal try bots.
+These try bots are a little different because they mirror waterfall bots that
+don't actually exist. The waterfall bots' specifications exist only to tell
+these try bots which tests to run.
 
-The optional try bots are a little different because they mirror waterfall bots
-that don't actually exist. The waterfall bots' specifications exist only to
-tell the optional try bots which tests to run.
+Let's say that you intended to add a new such custom try bot on Windows. Call it
+`win-myproject-rel` for example. You will need to add a "fake" mirror bot for
+each GPU family the tests you will need to run. For a GPU type of
+"CoolNewGPUType" in this example you could add a "fake" bot named "MyProject GPU
+Win10 Release (CoolNewGPUType)".
 
-Let's say that you intended to add a new such optional try bot on Windows. Call
-it `win_new_optional_tests_rel` for example. Now, if you wanted to just add
-this GPU type to the existing `win_optional_gpu_tests_rel` try bot, you'd
-just follow the instructions above
-([How to start running tests on a new GPU type on an existing try bot](#How-to-start-running-tests-on-a-new-GPU-type-on-an-existing-try-bot)). The steps below describe how to spin up
-an entire new optional try bot.
-
+1.  Allocate new virtual machines for the bots as described in [How to set up
+    new virtual machine
+    instances](#How-to-set-up-new-virtual-machine-instances).
 1.  Make sure that you have some swarming capacity for the new GPU type. Since
     it's not running against all Chromium CLs you don't need the recommended 30
     minimum bots, though ~10 would be good.
-1.  Create a CL in the Chromium workspace:
-    1.  Add your new bot (for example, "Optional Win7 Release
+1.  Create a CL in the Chromium workspace the does the following. Here's an
+    [example CL](https://crrev.com/c/1554296).
+    1.  Add your new bot (for example, "MyProject GPU Win10 Release
         (CoolNewGPUType)") to the chromium.gpu.fyi waterfall in
-        [waterfalls.pyl]. (Note, this is a bad example: the
-        "optional" bots have special semantics in this script. You'd probably
-        want to define some new category of bot if you didn't intend to add
-        this to `win_optional_gpu_tests_rel`.)
-    1.  Re-run the script to regenerate the JSON files.
-1.  Land the above CL.
-1.  Create a CL in the tools/build workspace:
-    1.  Modify `masters/master.tryserver.chromium.win`'s [master.cfg] and
-        [slaves.cfg] to add the new tryserver. Follow the pattern for the
-        existing `win_optional_gpu_tests_rel` tryserver. Namely, add the new
-        entry to master.cfg, and add the new tryserver to the
-        `optional_builders` list in `slaves.cfg`.
-    1.  Modify [`chromium_gpu_fyi.py`][chromium_gpu_fyi.py] to add the new
-        "Optional Win7 Release (CoolNewGPUType)" entry.
-    1.  Modify [`trybots.py`][trybots.py] to add
-        the new `win_new_optional_tests_rel` try bot, mirroring "Optional
-        Win7 Release (CoolNewGPUType)".
-1.  Land the above CL and request an off-hours restart of the
-    tryserver.chromium.win waterfall.
-1.  Now you can send CLs to the new bot with:
-    `git cl try -m tryserver.chromium.win -b win_new_optional_tests_rel`
-
-[master.cfg]: https://chromium.googlesource.com/chromium/tools/build/+/master/masters/master.tryserver.chromium.win/master.cfg
-[slaves.cfg]: https://chromium.googlesource.com/chromium/tools/build/+/master/masters/master.tryserver.chromium.win/slaves.cfg
+        [waterfalls.pyl].
+    1.  Re-run [`src/testing/buildbot/generate_buildbot_json.py`][generate_buildbot_json.py] to regenerate the JSON files.
+    1.  Update [`cr-buildbucket.cfg`][cr-buildbucket.cfg] to add `win-myproject-rel`.
+    1.  Update [`luci-milo.cfg`][luci-milo.cfg] to include `win-myproject-rel`.
+    1.  Update [`luci-scheduler.cfg`][luci-scheduler.cfg] to include "MyProject GPU Win10 Release
+        (CoolNewGPUType)".
+    1.  Update [`src/tools/mb/mb_config.pyl`][mb_config.pyl] to include `win-myproject-rel`.
+    1.  Also add your fake bot to [`src/testing/buildbot/generate_buildbot_json.py`][generate_buildbot_json.py] in the list of `get_bots_that_do_not_actually_exist` section.
+1. *After* the Chromium-side CL lands and the bot is on the console, create a CL
+    in the [`tools/build`][tools/build] workspace which does the
+    following. Here's an [example CL](https://crrev.com/c/1554272).
+    1.  Adds "MyProject GPU Win10 Release
+        (CoolNewGPUType)" to [`chromium_gpu_fyi.py`][chromium_gpu_fyi.py] in
+        `scripts/slave/recipe_modules/chromium_tests/`. You can copy a similar
+        step.
+    1.  Adds `win-myproject-rel` to [`trybots.py`][trybots.py] in the same folder.
+        This is where you associate "MyProject GPU Win10 Release
+        (CoolNewGPUType)" with `win-myproject-rel`. See the sample CL for an example.
+    1.  Get this reviewed and landed. This step tells the Chromium recipe about
+        the newly-deployed waterfall bot, so it knows which JSON file to load
+        out of src/testing/buildbot and which entry to look at.
+1.  After your CLs land you should be able to find and run `win-myproject-rel` on CLs
+    using Choose Trybots in Gerrit.
 
 ### How to test and deploy a driver update
 
diff --git a/docs/vscode.md b/docs/vscode.md
index b9d8ead2..ef87c75 100644
--- a/docs/vscode.md
+++ b/docs/vscode.md
@@ -154,24 +154,12 @@
     If you do not plan to use VSCode for debugging, vscode-clangd is a great
     alternative to C/C++ IntelliSense. It knows about how to compile Chromium,
     enabling it to provide smarter autocomplete than C/C++ IntelliSense as well
-    as allowing you to jump from functions to their definitions. To set it up:
+    as allowing you to jump from functions to their definitions. See
+    [clangd.md](clangd.md) for details.
 
-    1. Install vscode-clangd
-    2. Disable C/C++ IntelliSense
-    3. Generate compilation database, from chromium/src:
-
-        ```
-        $ ninja -C out/Default -t compdb cc cxx objc objcxx > compile_commands.json
-        ```
-
-    4. Re-run the above command each time you gclient sync to stay updated.
-
-    If you need to debug, disable the vscode-clangd plugin, enable C/C++
+    If you need to debug, disable the vscode-clangd extension, enable C/C++
     Intellisense, and restart VSCode.
 
-    Read more about [clangd with VSCode](https://clang.llvm.org/extra/clangd/Installation.html#editor-plugins).
-    If you are a Googler, also see [go/clangd-chromium](go/clangd-chromium).
-
 
 Also be sure to take a look at the
 [VS Code marketplace](https://marketplace.visualstudio.com/VSCode) to check out other
diff --git a/extensions/browser/extension_prefs.cc b/extensions/browser/extension_prefs.cc
index 2a02a01..cd01681 100644
--- a/extensions/browser/extension_prefs.cc
+++ b/extensions/browser/extension_prefs.cc
@@ -1869,7 +1869,7 @@
                                 true);
   registry->RegisterIntegerPref(kCorruptedDisableCount, 0);
   registry->RegisterBooleanPref(pref_names::kInsecureExtensionUpdatesEnabled,
-                                true);
+                                false);
 
 #if !defined(OS_MACOSX)
   registry->RegisterBooleanPref(pref_names::kAppFullscreenAllowed, true);
diff --git a/gpu/command_buffer/service/gpu_switches.cc b/gpu/command_buffer/service/gpu_switches.cc
index e1aedf5..ff0f8ae 100644
--- a/gpu/command_buffer/service/gpu_switches.cc
+++ b/gpu/command_buffer/service/gpu_switches.cc
@@ -83,4 +83,9 @@
 // used for present render result on screen.
 const char kDisableVulkanSurface[] = "disable-vulkan-surface";
 
+// Disables falling back to GL based hardware rendering if initializing Vulkan
+// fails. This is to allow tests to catch regressions in Vulkan.
+const char kDisableVulkanFallbackToGLForTesting[] =
+    "disable-vulkan-fallback-to-gl-for-testing";
+
 }  // namespace switches
diff --git a/gpu/command_buffer/service/gpu_switches.h b/gpu/command_buffer/service/gpu_switches.h
index 3f87de21..60b6a5ed 100644
--- a/gpu/command_buffer/service/gpu_switches.h
+++ b/gpu/command_buffer/service/gpu_switches.h
@@ -39,6 +39,7 @@
 GPU_EXPORT extern const char kEnableRasterToSkImage[];
 GPU_EXPORT extern const char kEnableVulkan[];
 GPU_EXPORT extern const char kDisableVulkanSurface[];
+GPU_EXPORT extern const char kDisableVulkanFallbackToGLForTesting[];
 
 }  // namespace switches
 
diff --git a/gpu/config/gpu_preferences.h b/gpu/config/gpu_preferences.h
index 5719fec..b866fbf 100644
--- a/gpu/config/gpu_preferences.h
+++ b/gpu/config/gpu_preferences.h
@@ -201,12 +201,18 @@
   // send a background/suspend signal.
   bool watchdog_starts_backgrounded = false;
 
+  // ===================================
+  // Settings from //gpu/command_buffer/service/gpu_switches.h
   // Use Vulkan for rasterization and display compositing.
   bool enable_vulkan = false;
 
   // Use vulkan VK_KHR_surface for presenting.
   bool disable_vulkan_surface = false;
 
+  // If Vulkan initialization has failed, do not fallback to GL. This is for
+  // testing in order to detect regressions which crash Vulkan.
+  bool disable_vulkan_fallback_to_gl_for_testing = false;
+
   // ===================================
   // Settings from //cc/base/switches.h
   // Enable the GPU benchmarking extension; used by tests only.
diff --git a/gpu/ipc/common/gpu_preferences.mojom b/gpu/ipc/common/gpu_preferences.mojom
index 3dd1f66..04b1901 100644
--- a/gpu/ipc/common/gpu_preferences.mojom
+++ b/gpu/ipc/common/gpu_preferences.mojom
@@ -66,6 +66,7 @@
   bool watchdog_starts_backgrounded;
   bool enable_vulkan;
   bool disable_vulkan_surface;
+  bool disable_vulkan_fallback_to_gl_for_testing;
   bool enable_gpu_benchmarking_extension;
   bool enable_webgpu;
 };
diff --git a/gpu/ipc/common/gpu_preferences_struct_traits.h b/gpu/ipc/common/gpu_preferences_struct_traits.h
index 9baa25c..107ba37 100644
--- a/gpu/ipc/common/gpu_preferences_struct_traits.h
+++ b/gpu/ipc/common/gpu_preferences_struct_traits.h
@@ -123,6 +123,8 @@
     out->watchdog_starts_backgrounded = prefs.watchdog_starts_backgrounded();
     out->enable_vulkan = prefs.enable_vulkan();
     out->disable_vulkan_surface = prefs.disable_vulkan_surface();
+    out->disable_vulkan_fallback_to_gl_for_testing =
+        prefs.disable_vulkan_fallback_to_gl_for_testing();
     out->enable_gpu_benchmarking_extension =
         prefs.enable_gpu_benchmarking_extension();
     out->enable_webgpu = prefs.enable_webgpu();
@@ -271,6 +273,10 @@
   static bool disable_vulkan_surface(const gpu::GpuPreferences& prefs) {
     return prefs.disable_vulkan_surface;
   }
+  static bool disable_vulkan_fallback_to_gl_for_testing(
+      const gpu::GpuPreferences& prefs) {
+    return prefs.disable_vulkan_fallback_to_gl_for_testing;
+  }
   static bool enable_gpu_benchmarking_extension(
       const gpu::GpuPreferences& prefs) {
     return prefs.enable_gpu_benchmarking_extension;
diff --git a/gpu/ipc/service/gpu_init.cc b/gpu/ipc/service/gpu_init.cc
index a138814..d0d64e8 100644
--- a/gpu/ipc/service/gpu_init.cc
+++ b/gpu/ipc/service/gpu_init.cc
@@ -235,6 +235,7 @@
             !gpu_preferences_.disable_vulkan_surface)) {
       DLOG(WARNING) << "Failed to create and initialize Vulkan implementation.";
       vulkan_implementation_ = nullptr;
+      CHECK(!gpu_preferences_.disable_vulkan_fallback_to_gl_for_testing);
     }
     gpu_preferences_.enable_vulkan = !!vulkan_implementation_;
   }
diff --git a/infra/config/cr-buildbucket.cfg b/infra/config/cr-buildbucket.cfg
index c42f673..3345a58 100644
--- a/infra/config/cr-buildbucket.cfg
+++ b/infra/config/cr-buildbucket.cfg
@@ -3506,6 +3506,7 @@
       name: "gpu-manual-try-linux-nvidia-tsn"
     }
     builders { mixins: "linux-try" name: "leak_detection_linux" }
+    builders { mixins: "linux-angle-try" name: "linux-angle-rel" }
     builders { mixins: "linux-angle-try" name: "linux_angle_compile_dbg_ng" }
     builders { mixins: "linux-angle-try" name: "linux_angle_dbg_ng" }
     builders { mixins: "linux-angle-try" name: "linux_angle_deqp_rel_ng" }
@@ -3625,6 +3626,7 @@
     builders { mixins: "ios-try" name: "ios-simulator-eg" }
     builders { mixins: "ios-try" name: "ios-simulator-xcode-clang" }
     builders { mixins: "ios-try" name: "ios-slimnav" }
+    builders { mixins: "mac-angle-try" name: "mac-angle-rel" }
     builders { mixins: "mac-angle-try" name: "mac_angle_compile_dbg_ng" }
     builders { mixins: "mac-angle-try" name: "mac_angle_dbg_ng" }
     builders { mixins: "mac-angle-try" name: "mac_angle_rel_ng" }
diff --git a/infra/config/luci-milo.cfg b/infra/config/luci-milo.cfg
index ba67371..79a77ad 100644
--- a/infra/config/luci-milo.cfg
+++ b/infra/config/luci-milo.cfg
@@ -4478,6 +4478,9 @@
     name: "buildbucket/luci.chromium.try/android_angle_vk64_rel_ng"
   }
   builders {
+    name: "buildbucket/luci.chromium.try/linux-angle-rel"
+  }
+  builders {
     name: "buildbucket/luci.chromium.try/linux_angle_dbg_ng"
   }
   builders {
@@ -4490,6 +4493,9 @@
     name: "buildbucket/luci.chromium.try/linux_angle_rel_ng"
   }
   builders {
+    name: "buildbucket/luci.chromium.try/mac-angle-rel"
+  }
+  builders {
     name: "buildbucket/luci.chromium.try/mac_angle_dbg_ng"
   }
   builders {
@@ -4719,6 +4725,9 @@
     name: "buildbucket/luci.chromium.try/gpu-manual-try-win10-nvidia-rel"
   }
   builders {
+    name: "buildbucket/luci.chromium.try/linux-angle-rel"
+  }
+  builders {
     name: "buildbucket/luci.chromium.try/linux-blink-heap-concurrent-marking-tsan-rel"
   }
   builders {
@@ -4858,6 +4867,9 @@
     name: "buildbucket/luci.chromium.try/ios12-sdk-simulator"
   }
   builders {
+    name: "buildbucket/luci.chromium.try/mac-angle-rel"
+  }
+  builders {
     name: "buildbucket/luci.chromium.try/mac-jumbo-rel"
   }
   builders {
diff --git a/infra/config/luci-scheduler.cfg b/infra/config/luci-scheduler.cfg
index 5c6d9817..e456176 100644
--- a/infra/config/luci-scheduler.cfg
+++ b/infra/config/luci-scheduler.cfg
@@ -1312,6 +1312,22 @@
 ################################################################################
 
 job {
+  id: "ANGLE GPU Linux Release (Intel HD 630)"
+  # Triggered by "GPU FYI Linux Builder".
+  acl_sets: "triggered-by-parent-builders"
+  # This bot doesn't actually exist, so it's noop'ed out. crbug.com/819899
+  noop: {}
+}
+
+job {
+  id: "ANGLE GPU Linux Release (NVIDIA)"
+  # Triggered by "GPU FYI Linux Builder".
+  acl_sets: "triggered-by-parent-builders"
+  # This bot doesn't actually exist, so it's noop'ed out. crbug.com/819899
+  noop: {}
+}
+
+job {
   id: "Cast Audio Linux"
   acl_sets: "default"
   buildbucket: {
@@ -1985,6 +2001,30 @@
 ################################################################################
 
 job {
+  id: "ANGLE GPU Mac Release (Intel)"
+  # Triggered by "GPU FYI Mac Builder".
+  acl_sets: "triggered-by-parent-builders"
+  # This bot doesn't actually exist, so it's noop'ed out. crbug.com/819899
+  noop: {}
+}
+
+job {
+  id: "ANGLE GPU Mac Retina Release (NVIDIA)"
+  # Triggered by "GPU FYI Mac Builder".
+  acl_sets: "triggered-by-parent-builders"
+  # This bot doesn't actually exist, so it's noop'ed out. crbug.com/819899
+  noop: {}
+}
+
+job {
+  id: "ANGLE GPU Mac Retina Release (AMD)"
+  # Triggered by "GPU FYI Mac Builder".
+  acl_sets: "triggered-by-parent-builders"
+  # This bot doesn't actually exist, so it's noop'ed out. crbug.com/819899
+  noop: {}
+}
+
+job {
   id: "Dawn GPU Mac Release (Intel)"
   # Triggered by "GPU FYI Mac Builder".
   acl_sets: "triggered-by-parent-builders"
diff --git a/ios/chrome/app/resources/ios_resources.grd b/ios/chrome/app/resources/ios_resources.grd
index 1aa2035..69df3a0 100644
--- a/ios/chrome/app/resources/ios_resources.grd
+++ b/ios/chrome/app/resources/ios_resources.grd
@@ -14,6 +14,8 @@
       <include name="IDR_IOS_OMAHA_JS" file="omaha/omaha.js" type="BINDATA" compress="gzip" />
       <include name="IDR_IOS_UKM_INTERNALS_HTML" file="../../../../components/ukm/debug/ukm_internals.html" flattenhtml="true" allowexternalscript="true" compress="gzip" type="BINDATA" />
       <include name="IDR_IOS_UKM_INTERNALS_JS" file="../../../../components/ukm/debug/ukm_internals.js" flattenhtml="true" compress="gzip" type="BINDATA" />
+      <include name="IDR_IOS_TRANSLATE_INTERNALS_HTML" file="../../../../components/translate/translate_internals/translate_internals.html" flattenhtml="true" allowexternalscript="true" compress="gzip" type="BINDATA" />
+      <include name="IDR_IOS_TRANSLATE_INTERNALS_JS" file="../../../../components/translate/translate_internals/translate_internals.js" flattenhtml="true" compress="gzip" type="BINDATA" />
     </includes>
   </release>
 </grit>
diff --git a/ios/chrome/browser/DEPS b/ios/chrome/browser/DEPS
index e90b9876..56c7157 100644
--- a/ios/chrome/browser/DEPS
+++ b/ios/chrome/browser/DEPS
@@ -87,8 +87,7 @@
   "+components/sync_sessions",
   "+components/sync_preferences",
   "+components/task_scheduler_util",
-  "+components/translate/core",
-  "+components/translate/ios",
+  "+components/translate",
   "+components/ui_metrics",
   "+components/ukm",
   "+components/undo",
diff --git a/ios/chrome/browser/chrome_url_constants.cc b/ios/chrome/browser/chrome_url_constants.cc
index 2576ec4..bcb79ab6 100644
--- a/ios/chrome/browser/chrome_url_constants.cc
+++ b/ios/chrome/browser/chrome_url_constants.cc
@@ -47,6 +47,7 @@
 const char kChromeUISuggestionsHost[] = "suggestions";
 const char kChromeUISyncInternalsHost[] = "sync-internals";
 const char kChromeUITermsHost[] = "terms";
+const char kChromeUITranslateInternalsHost[] = "translate-internals";
 const char kChromeUIURLKeyedMetricsHost[] = "ukm";
 const char kChromeUIUserActionsHost[] = "user-actions";
 const char kChromeUIVersionHost[] = "version";
diff --git a/ios/chrome/browser/chrome_url_constants.h b/ios/chrome/browser/chrome_url_constants.h
index 57af229a..96a19ca4 100644
--- a/ios/chrome/browser/chrome_url_constants.h
+++ b/ios/chrome/browser/chrome_url_constants.h
@@ -59,6 +59,7 @@
 extern const char kChromeUISuggestionsHost[];
 extern const char kChromeUISyncInternalsHost[];
 extern const char kChromeUITermsHost[];
+extern const char kChromeUITranslateInternalsHost[];
 extern const char kChromeUIURLKeyedMetricsHost[];
 extern const char kChromeUIUserActionsHost[];
 extern const char kChromeUIVersionHost[];
diff --git a/ios/chrome/browser/ui/autofill/cells/BUILD.gn b/ios/chrome/browser/ui/autofill/cells/BUILD.gn
index c981086..23fee9c 100644
--- a/ios/chrome/browser/ui/autofill/cells/BUILD.gn
+++ b/ios/chrome/browser/ui/autofill/cells/BUILD.gn
@@ -15,7 +15,6 @@
   ]
 
   deps = [
-    "resources:autofill_edit_item_icon",
     "//components/resources",
     "//components/strings",
     "//ios/chrome/app/strings",
@@ -42,7 +41,6 @@
 source_set("unit_tests") {
   testonly = true
   sources = [
-    "autofill_edit_item_unittest.mm",
     "cvc_item_unittest.mm",
     "legacy_autofill_edit_item_unittest.mm",
     "status_item_unittest.mm",
diff --git a/ios/chrome/browser/ui/autofill/cells/autofill_edit_item.h b/ios/chrome/browser/ui/autofill/cells/autofill_edit_item.h
index 5e175cb..4da8a62 100644
--- a/ios/chrome/browser/ui/autofill/cells/autofill_edit_item.h
+++ b/ios/chrome/browser/ui/autofill/cells/autofill_edit_item.h
@@ -8,60 +8,14 @@
 #import <UIKit/UIKit.h>
 
 #import "ios/chrome/browser/ui/autofill/autofill_ui_type.h"
-#import "ios/chrome/browser/ui/table_view/cells/table_view_item.h"
+#import "ios/chrome/browser/ui/table_view/cells/table_view_text_edit_item.h"
 
-// Item to represent and configure an AutofillEditItem. It features a label and
-// a text field.
-@interface AutofillEditItem : TableViewItem
-
-// The name of the text field.
-@property(nonatomic, copy) NSString* textFieldName;
-
-// The value of the text field.
-@property(nonatomic, copy) NSString* textFieldValue;
-
-// An icon identifying the text field or its current value, if any.
-@property(nonatomic, copy) UIImage* identifyingIcon;
+// Item to represent and configure an AutofillEditItem.
+@interface AutofillEditItem : TableViewTextEditItem
 
 // The field type this item is describing.
 @property(nonatomic, assign) AutofillUIType autofillUIType;
 
-// Whether this field is required. If YES, an "*" is appended to the name of the
-// text field to indicate that the field is required. It is also used for
-// validation purposes.
-@property(nonatomic, getter=isRequired) BOOL required;
-
-// Whether the text field is enabled for editing.
-@property(nonatomic, getter=isTextFieldEnabled) BOOL textFieldEnabled;
-
-// Controls the display of the return key when the keyboard is displaying.
-@property(nonatomic, assign) UIReturnKeyType returnKeyType;
-
-// Keyboard type to be displayed when the text field becomes first responder.
-@property(nonatomic, assign) UIKeyboardType keyboardType;
-
-// Controls autocapitalization behavior of the text field.
-@property(nonatomic, assign)
-    UITextAutocapitalizationType autoCapitalizationType;
-
-@end
-
-// AutofillEditCell implements an TableViewCell subclass containing a label
-// and a text field.
-@interface AutofillEditCell : TableViewCell
-
-// Label at the leading edge of the cell. It displays the item's textFieldName.
-@property(nonatomic, strong) UILabel* textLabel;
-
-// Text field at the trailing edge of the cell. It displays the item's
-// |textFieldValue|.
-@property(nonatomic, readonly, strong) UITextField* textField;
-
-// Whether the icon showing that the cell is editable should be displayed.
-@property(nonatomic, assign) BOOL editIconDisplayed;
-
-- (void)setIdentifyingIcon:(UIImage*)icon;
-
 @end
 
 #endif  // IOS_CHROME_BROWSER_UI_AUTOFILL_CELLS_AUTOFILL_EDIT_ITEM_H_
diff --git a/ios/chrome/browser/ui/autofill/cells/autofill_edit_item.mm b/ios/chrome/browser/ui/autofill/cells/autofill_edit_item.mm
index 38c1f49..91de3674 100644
--- a/ios/chrome/browser/ui/autofill/cells/autofill_edit_item.mm
+++ b/ios/chrome/browser/ui/autofill/cells/autofill_edit_item.mm
@@ -4,321 +4,20 @@
 
 #import "ios/chrome/browser/ui/autofill/cells/autofill_edit_item.h"
 
-#import "ios/chrome/browser/ui/table_view/cells/table_view_cells_constants.h"
-#import "ios/chrome/browser/ui/table_view/chrome_table_view_styler.h"
 #include "ios/chrome/browser/ui/ui_feature_flags.h"
-#import "ios/chrome/browser/ui/util/rtl_geometry.h"
-#import "ios/chrome/browser/ui/util/uikit_ui_util.h"
-#import "ios/chrome/common/ui_util/constraints_ui_util.h"
 
 #if !defined(__has_feature) || !__has_feature(objc_arc)
 #error "This file requires ARC support."
 #endif
 
-namespace {
-// Minimum gap between the label and the text field.
-const CGFloat kLabelAndFieldGap = 5;
-// Height/width of the edit icon.
-const CGFloat kEditIconLength = 18;
-}  // namespace
-
 @implementation AutofillEditItem
 
 - (instancetype)initWithType:(NSInteger)type {
   self = [super initWithType:type];
   if (self) {
-    self.cellClass = [AutofillEditCell class];
-    _returnKeyType = UIReturnKeyNext;
-    _keyboardType = UIKeyboardTypeDefault;
-    _autoCapitalizationType = UITextAutocapitalizationTypeWords;
+    self.hideEditIcon = !base::FeatureList::IsEnabled(kSettingsRefresh);
   }
   return self;
 }
 
-#pragma mark TableViewItem
-
-- (void)configureCell:(AutofillEditCell*)cell
-           withStyler:(ChromeTableViewStyler*)styler {
-  [super configureCell:cell withStyler:styler];
-
-  NSString* textLabelFormat = self.required ? @"%@*" : @"%@";
-  cell.textLabel.text =
-      [NSString stringWithFormat:textLabelFormat, self.textFieldName];
-  cell.textField.text = self.textFieldValue;
-  if (self.textFieldName.length) {
-    cell.textField.accessibilityIdentifier =
-        [NSString stringWithFormat:@"%@_textField", self.textFieldName];
-  }
-  if (styler.cellBackgroundColor) {
-    cell.textLabel.backgroundColor = styler.cellBackgroundColor;
-    cell.textField.backgroundColor = styler.cellBackgroundColor;
-  } else {
-    cell.textLabel.backgroundColor = styler.tableViewBackgroundColor;
-    cell.textField.backgroundColor = styler.tableViewBackgroundColor;
-  }
-  cell.textField.enabled = self.textFieldEnabled;
-  if (base::FeatureList::IsEnabled(kSettingsRefresh)) {
-    cell.textField.textColor =
-        UIColorFromRGB(kTableViewSecondaryLabelLightGrayTextColor);
-    cell.editIconDisplayed = self.textFieldEnabled;
-  } else {
-    cell.textField.textColor =
-        self.textFieldEnabled
-            ? UIColorFromRGB(kTableViewTextLabelColorBlue)
-            : UIColorFromRGB(kTableViewSecondaryLabelLightGrayTextColor);
-  }
-  [cell.textField addTarget:self
-                     action:@selector(textFieldChanged:)
-           forControlEvents:UIControlEventEditingChanged];
-  cell.textField.returnKeyType = self.returnKeyType;
-  cell.textField.keyboardType = self.keyboardType;
-  cell.textField.autocapitalizationType = self.autoCapitalizationType;
-  [cell setIdentifyingIcon:self.identifyingIcon];
-}
-
-#pragma mark Actions
-
-- (void)textFieldChanged:(UITextField*)textField {
-  self.textFieldValue = textField.text;
-}
-
-@end
-
-#pragma mark - AutofillEditCell
-
-@interface AutofillEditCell ()
-
-@property(nonatomic, strong) NSLayoutConstraint* iconHeightConstraint;
-@property(nonatomic, strong) NSLayoutConstraint* iconWidthConstraint;
-@property(nonatomic, strong) NSLayoutConstraint* textFieldTrailingConstraint;
-@property(nonatomic, strong) NSLayoutConstraint* textLabelTrailingConstraint;
-
-@property(nonatomic, strong) NSLayoutConstraint* editIconHeightConstraint;
-@property(nonatomic, strong) NSLayoutConstraint* iconTrailingConstraint;
-
-// When they are activated, the label and the text field are on one line.
-// They conflict with the |accessibilityConstraints|.
-@property(nonatomic, strong) NSArray<NSLayoutConstraint*>* standardConstraints;
-// When they are activated, the label is on one line, the text field is on
-// another line. They conflict with the |standardConstraints|.
-@property(nonatomic, strong)
-    NSArray<NSLayoutConstraint*>* accessibilityConstraints;
-
-// UIImageView containing the icon identifying |textField| or its current value.
-@property(nonatomic, readonly, strong) UIImageView* identifyingIconView;
-
-// UIImageView containing the icon indicating that |textField| is editable.
-@property(nonatomic, strong) UIImageView* editIconView;
-
-@end
-
-@implementation AutofillEditCell
-
-@synthesize textLabel = _textLabel;
-
-- (instancetype)initWithStyle:(UITableViewCellStyle)style
-              reuseIdentifier:(NSString*)reuseIdentifier {
-  self = [super initWithStyle:style reuseIdentifier:reuseIdentifier];
-  if (self) {
-    self.isAccessibilityElement = YES;
-    UIView* contentView = self.contentView;
-
-    _textLabel = [[UILabel alloc] init];
-    _textLabel.translatesAutoresizingMaskIntoConstraints = NO;
-    [_textLabel setContentHuggingPriority:UILayoutPriorityDefaultHigh
-                                  forAxis:UILayoutConstraintAxisHorizontal];
-    _textLabel.font = [UIFont preferredFontForTextStyle:UIFontTextStyleBody];
-    _textLabel.adjustsFontForContentSizeCategory = YES;
-    [contentView addSubview:_textLabel];
-
-    _textField = [[UITextField alloc] init];
-    _textField.translatesAutoresizingMaskIntoConstraints = NO;
-    _textField.font = [UIFont preferredFontForTextStyle:UIFontTextStyleBody];
-    _textField.adjustsFontForContentSizeCategory = YES;
-    [_textField
-        setContentCompressionResistancePriority:UILayoutPriorityDefaultLow
-                                        forAxis:
-                                            UILayoutConstraintAxisHorizontal];
-    [contentView addSubview:_textField];
-
-    _textField.autocorrectionType = UITextAutocorrectionTypeNo;
-    _textField.clearButtonMode = UITextFieldViewModeWhileEditing;
-    _textField.contentVerticalAlignment =
-        UIControlContentVerticalAlignmentCenter;
-
-    // Card type icon.
-    _identifyingIconView = [[UIImageView alloc] initWithFrame:CGRectZero];
-    _identifyingIconView.translatesAutoresizingMaskIntoConstraints = NO;
-    [contentView addSubview:_identifyingIconView];
-
-    // Edit icon.
-    UIImage* editImage = [[UIImage imageNamed:@"autofill_edit_item_icon"]
-        imageWithRenderingMode:UIImageRenderingModeAlwaysTemplate];
-    _editIconView = [[UIImageView alloc] initWithImage:editImage];
-    _editIconView.tintColor =
-        UIColorFromRGB(kTableViewSecondaryLabelLightGrayTextColor);
-    _editIconView.translatesAutoresizingMaskIntoConstraints = NO;
-    [contentView addSubview:_editIconView];
-
-    // Set up the icons size constraints. They are activated here and updated in
-    // layoutSubviews.
-    _iconHeightConstraint =
-        [_identifyingIconView.heightAnchor constraintEqualToConstant:0];
-    _iconWidthConstraint =
-        [_identifyingIconView.widthAnchor constraintEqualToConstant:0];
-    _editIconHeightConstraint =
-        [_editIconView.heightAnchor constraintEqualToConstant:0];
-
-    _textFieldTrailingConstraint = [_textField.trailingAnchor
-        constraintEqualToAnchor:_editIconView.leadingAnchor];
-    _textLabelTrailingConstraint = [_textLabel.trailingAnchor
-        constraintEqualToAnchor:_editIconView.leadingAnchor];
-    _iconTrailingConstraint = [_editIconView.trailingAnchor
-        constraintEqualToAnchor:_identifyingIconView.leadingAnchor];
-
-    _standardConstraints = @[
-      [_textField.firstBaselineAnchor
-          constraintEqualToAnchor:_textLabel.firstBaselineAnchor],
-      [_textField.leadingAnchor
-          constraintEqualToAnchor:_textLabel.trailingAnchor
-                         constant:kLabelAndFieldGap],
-    ];
-
-    _accessibilityConstraints = @[
-      [_textField.topAnchor constraintEqualToAnchor:_textLabel.bottomAnchor
-                                           constant:kTableViewVerticalSpacing],
-      [_textField.leadingAnchor
-          constraintEqualToAnchor:contentView.leadingAnchor
-                         constant:kTableViewHorizontalSpacing],
-      _textLabelTrailingConstraint,
-    ];
-
-    // Set up the constraints.
-    [NSLayoutConstraint activateConstraints:@[
-      [_textLabel.leadingAnchor
-          constraintEqualToAnchor:contentView.leadingAnchor
-                         constant:kTableViewHorizontalSpacing],
-      _textFieldTrailingConstraint,
-      [_identifyingIconView.trailingAnchor
-          constraintEqualToAnchor:contentView.trailingAnchor
-                         constant:-kTableViewHorizontalSpacing],
-      [_identifyingIconView.centerYAnchor
-          constraintEqualToAnchor:contentView.centerYAnchor],
-      [_editIconView.centerYAnchor
-          constraintEqualToAnchor:contentView.centerYAnchor],
-      _iconHeightConstraint,
-      _iconWidthConstraint,
-      _iconTrailingConstraint,
-      _editIconHeightConstraint,
-      [_editIconView.widthAnchor
-          constraintEqualToAnchor:_editIconView.heightAnchor],
-    ]];
-    AddOptionalVerticalPadding(contentView, _textLabel,
-                               kTableViewOneLabelCellVerticalSpacing);
-    AddOptionalVerticalPadding(contentView, _textField,
-                               kTableViewOneLabelCellVerticalSpacing);
-
-    [self updateForAccessibilityContentSizeCategory:
-              UIContentSizeCategoryIsAccessibilityCategory(
-                  self.traitCollection.preferredContentSizeCategory)];
-  }
-  return self;
-}
-
-#pragma mark Public
-
-- (void)setEditIconDisplayed:(BOOL)editIconDisplayed {
-  if (editIconDisplayed == _editIconDisplayed)
-    return;
-
-  _editIconDisplayed = editIconDisplayed;
-  self.editIconView.hidden = !editIconDisplayed;
-  if (editIconDisplayed) {
-    self.textFieldTrailingConstraint.constant = -kLabelAndFieldGap;
-    self.textLabelTrailingConstraint.constant = -kLabelAndFieldGap;
-
-    _editIconHeightConstraint.constant = kEditIconLength;
-  } else {
-    self.textFieldTrailingConstraint.constant = 0;
-    self.textLabelTrailingConstraint.constant = 0;
-
-    _editIconHeightConstraint.constant = 0;
-  }
-}
-
-- (void)setIdentifyingIcon:(UIImage*)icon {
-  self.identifyingIconView.image = icon;
-  if (icon) {
-    self.iconTrailingConstraint.constant = -kLabelAndFieldGap;
-
-    // Set the size constraints of the icon view to the dimensions of the image.
-    self.iconHeightConstraint.constant = icon.size.height;
-    self.iconWidthConstraint.constant = icon.size.width;
-  } else {
-    self.iconTrailingConstraint.constant = 0;
-    self.iconHeightConstraint.constant = 0;
-    self.iconWidthConstraint.constant = 0;
-  }
-}
-
-- (void)traitCollectionDidChange:(UITraitCollection*)previousTraitCollection {
-  [super traitCollectionDidChange:previousTraitCollection];
-  BOOL isCurrentCategoryAccessibility =
-      UIContentSizeCategoryIsAccessibilityCategory(
-          self.traitCollection.preferredContentSizeCategory);
-  if (isCurrentCategoryAccessibility !=
-      UIContentSizeCategoryIsAccessibilityCategory(
-          previousTraitCollection.preferredContentSizeCategory)) {
-    [self updateForAccessibilityContentSizeCategory:
-              isCurrentCategoryAccessibility];
-  }
-}
-
-#pragma mark - UITableViewCell
-
-- (void)prepareForReuse {
-  [super prepareForReuse];
-  self.textLabel.text = nil;
-  self.textField.text = nil;
-  self.textField.returnKeyType = UIReturnKeyNext;
-  self.textField.keyboardType = UIKeyboardTypeDefault;
-  self.textField.autocapitalizationType = UITextAutocapitalizationTypeWords;
-  self.textField.autocorrectionType = UITextAutocorrectionTypeNo;
-  self.textField.clearButtonMode = UITextFieldViewModeWhileEditing;
-  self.textField.accessibilityIdentifier = nil;
-  self.textField.enabled = NO;
-  self.textField.delegate = nil;
-  [self.textField removeTarget:nil
-                        action:nil
-              forControlEvents:UIControlEventAllEvents];
-  self.identifyingIconView.image = nil;
-}
-
-#pragma mark Accessibility
-
-- (NSString*)accessibilityLabel {
-  return [NSString
-      stringWithFormat:@"%@, %@", self.textLabel.text, self.textField.text];
-}
-
-#pragma mark Private
-
-// Updates the cell such as it is layouted correctly with regard to the
-// preferred content size category, if it is an
-// |accessibilityContentSizeCategory| or not.
-- (void)updateForAccessibilityContentSizeCategory:
-    (BOOL)accessibilityContentSizeCategory {
-  if (accessibilityContentSizeCategory) {
-    [NSLayoutConstraint deactivateConstraints:_standardConstraints];
-    [NSLayoutConstraint activateConstraints:_accessibilityConstraints];
-    _textField.textAlignment =
-        UseRTLLayout() ? NSTextAlignmentRight : NSTextAlignmentLeft;
-  } else {
-    [NSLayoutConstraint deactivateConstraints:_accessibilityConstraints];
-    [NSLayoutConstraint activateConstraints:_standardConstraints];
-    _textField.textAlignment =
-        UseRTLLayout() ? NSTextAlignmentLeft : NSTextAlignmentRight;
-  }
-}
-
 @end
diff --git a/ios/chrome/browser/ui/autofill/cells/autofill_edit_item_unittest.mm b/ios/chrome/browser/ui/autofill/cells/autofill_edit_item_unittest.mm
deleted file mode 100644
index b6529c33..0000000
--- a/ios/chrome/browser/ui/autofill/cells/autofill_edit_item_unittest.mm
+++ /dev/null
@@ -1,42 +0,0 @@
-// Copyright 2018 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#import "ios/chrome/browser/ui/autofill/cells/autofill_edit_item.h"
-
-#import "ios/chrome/browser/ui/table_view/chrome_table_view_styler.h"
-#include "testing/gtest/include/gtest/gtest.h"
-#import "testing/gtest_mac.h"
-#include "testing/platform_test.h"
-
-#if !defined(__has_feature) || !__has_feature(objc_arc)
-#error "This file requires ARC support."
-#endif
-
-using AutofillEditItemTest = PlatformTest;
-
-// Tests that the label and text field are set properly after a call to
-// |configureCell:|.
-TEST_F(AutofillEditItemTest, ConfigureCell) {
-  AutofillEditItem* item = [[AutofillEditItem alloc] initWithType:0];
-  NSString* name = @"Name";
-  NSString* value = @"Value";
-  BOOL enabled = NO;
-
-  item.textFieldName = name;
-  item.textFieldValue = value;
-  item.textFieldEnabled = enabled;
-
-  id cell = [[[item cellClass] alloc] init];
-  ASSERT_TRUE([cell isMemberOfClass:[AutofillEditCell class]]);
-
-  AutofillEditCell* autofillEditCell = cell;
-  EXPECT_EQ(0U, autofillEditCell.textLabel.text.length);
-  EXPECT_EQ(0U, autofillEditCell.textField.text.length);
-  EXPECT_TRUE(autofillEditCell.textField.enabled);
-
-  [item configureCell:cell withStyler:[[ChromeTableViewStyler alloc] init]];
-  EXPECT_NSEQ(name, autofillEditCell.textLabel.text);
-  EXPECT_NSEQ(value, autofillEditCell.textField.text);
-  EXPECT_FALSE(autofillEditCell.textField.enabled);
-}
diff --git a/ios/chrome/browser/ui/autofill/cells/resources/BUILD.gn b/ios/chrome/browser/ui/autofill/cells/resources/BUILD.gn
deleted file mode 100644
index 5695705..0000000
--- a/ios/chrome/browser/ui/autofill/cells/resources/BUILD.gn
+++ /dev/null
@@ -1,13 +0,0 @@
-# Copyright 2019 The Chromium Authors. All rights reserved.
-# Use of this source code is governed by a BSD-style license that can be
-# found in the LICENSE file.
-
-import("//build/config/ios/asset_catalog.gni")
-
-imageset("autofill_edit_item_icon") {
-  sources = [
-    "autofill_edit_item_icon.imageset/Contents.json",
-    "autofill_edit_item_icon.imageset/autofill_edit_item_icon@2x.png",
-    "autofill_edit_item_icon.imageset/autofill_edit_item_icon@3x.png",
-  ]
-}
diff --git a/ios/chrome/browser/ui/settings/BUILD.gn b/ios/chrome/browser/ui/settings/BUILD.gn
index c50247b..f2fe346 100644
--- a/ios/chrome/browser/ui/settings/BUILD.gn
+++ b/ios/chrome/browser/ui/settings/BUILD.gn
@@ -125,6 +125,7 @@
     "//ios/chrome/browser/browser_state",
     "//ios/chrome/browser/browser_state:browser_state_impl",
     "//ios/chrome/browser/browsing_data",
+    "//ios/chrome/browser/browsing_data:feature_flags",
     "//ios/chrome/browser/content_settings",
     "//ios/chrome/browser/favicon",
     "//ios/chrome/browser/feature_engagement",
diff --git a/ios/chrome/browser/ui/settings/autofill/autofill_credit_card_edit_table_view_controller.mm b/ios/chrome/browser/ui/settings/autofill/autofill_credit_card_edit_table_view_controller.mm
index ba5eaf3f..542e14b 100644
--- a/ios/chrome/browser/ui/settings/autofill/autofill_credit_card_edit_table_view_controller.mm
+++ b/ios/chrome/browser/ui/settings/autofill/autofill_credit_card_edit_table_view_controller.mm
@@ -260,7 +260,8 @@
   cell.selectionStyle = UITableViewCellSelectionStyleNone;
 
   NSInteger itemType = [self.tableViewModel itemTypeForIndexPath:indexPath];
-  AutofillEditCell* editCell = base::mac::ObjCCast<AutofillEditCell>(cell);
+  TableViewTextEditCell* editCell =
+      base::mac::ObjCCast<TableViewTextEditCell>(cell);
   editCell.textField.delegate = self;
   switch (itemType) {
     case ItemTypeCardholderName:
@@ -296,8 +297,8 @@
     didSelectRowAtIndexPath:(NSIndexPath*)indexPath {
   if (self.tableView.editing) {
     UITableViewCell* cell = [self.tableView cellForRowAtIndexPath:indexPath];
-    AutofillEditCell* textFieldCell =
-        base::mac::ObjCCastStrict<AutofillEditCell>(cell);
+    TableViewTextEditCell* textFieldCell =
+        base::mac::ObjCCastStrict<TableViewTextEditCell>(cell);
     [textFieldCell.textField becomeFirstResponder];
   }
 }
diff --git a/ios/chrome/browser/ui/settings/autofill/autofill_edit_table_view_controller.mm b/ios/chrome/browser/ui/settings/autofill/autofill_edit_table_view_controller.mm
index 3a0ee7b..7409f5a 100644
--- a/ios/chrome/browser/ui/settings/autofill/autofill_edit_table_view_controller.mm
+++ b/ios/chrome/browser/ui/settings/autofill/autofill_edit_table_view_controller.mm
@@ -15,7 +15,7 @@
 #endif
 
 @interface AutofillEditTableViewController () <AutofillEditAccessoryDelegate> {
-  AutofillEditCell* _currentEditingCell;
+  TableViewTextEditCell* _currentEditingCell;
   AutofillEditAccessoryView* _accessoryView;
 }
 @end
@@ -70,14 +70,14 @@
 #pragma mark - UITextFieldDelegate
 
 - (void)textFieldDidBeginEditing:(UITextField*)textField {
-  AutofillEditCell* cell = [self autofillEditCellForTextField:textField];
+  TableViewTextEditCell* cell = [self autofillEditCellForTextField:textField];
   _currentEditingCell = cell;
   [textField setInputAccessoryView:_accessoryView];
   [self updateAccessoryViewButtonState];
 }
 
 - (void)textFieldDidEndEditing:(UITextField*)textField {
-  AutofillEditCell* cell = [self autofillEditCellForTextField:textField];
+  TableViewTextEditCell* cell = [self autofillEditCellForTextField:textField];
   DCHECK(_currentEditingCell == cell);
   [textField setInputAccessoryView:nil];
   _currentEditingCell = nil;
@@ -106,10 +106,11 @@
 #pragma mark - Helper methods
 
 // Returns the cell containing |textField|.
-- (AutofillEditCell*)autofillEditCellForTextField:(UITextField*)textField {
-  AutofillEditCell* settingsCell = nil;
+- (TableViewTextEditCell*)autofillEditCellForTextField:(UITextField*)textField {
+  TableViewTextEditCell* settingsCell = nil;
   for (UIView* view = textField; view; view = [view superview]) {
-    AutofillEditCell* cell = base::mac::ObjCCast<AutofillEditCell>(view);
+    TableViewTextEditCell* cell =
+        base::mac::ObjCCast<TableViewTextEditCell>(view);
     if (cell) {
       settingsCell = cell;
       break;
@@ -155,8 +156,9 @@
   if (!nextCellPath) {
     [[_currentEditingCell textField] resignFirstResponder];
   } else {
-    AutofillEditCell* nextCell = base::mac::ObjCCastStrict<AutofillEditCell>(
-        [self.tableView cellForRowAtIndexPath:nextCellPath]);
+    TableViewTextEditCell* nextCell =
+        base::mac::ObjCCastStrict<TableViewTextEditCell>(
+            [self.tableView cellForRowAtIndexPath:nextCellPath]);
     [nextCell.textField becomeFirstResponder];
   }
 }
diff --git a/ios/chrome/browser/ui/settings/autofill/autofill_profile_edit_table_view_controller.mm b/ios/chrome/browser/ui/settings/autofill/autofill_profile_edit_table_view_controller.mm
index 30b98159..4d2364b 100644
--- a/ios/chrome/browser/ui/settings/autofill/autofill_profile_edit_table_view_controller.mm
+++ b/ios/chrome/browser/ui/settings/autofill/autofill_profile_edit_table_view_controller.mm
@@ -220,8 +220,8 @@
 
   cell.selectionStyle = UITableViewCellSelectionStyleNone;
 
-  AutofillEditCell* textFieldCell =
-      base::mac::ObjCCastStrict<AutofillEditCell>(cell);
+  TableViewTextEditCell* textFieldCell =
+      base::mac::ObjCCastStrict<TableViewTextEditCell>(cell);
   textFieldCell.accessibilityIdentifier = textFieldCell.textLabel.text;
   textFieldCell.textField.delegate = self;
   return textFieldCell;
@@ -231,8 +231,8 @@
     didSelectRowAtIndexPath:(NSIndexPath*)indexPath {
   if (self.tableView.editing) {
     UITableViewCell* cell = [self.tableView cellForRowAtIndexPath:indexPath];
-    AutofillEditCell* textFieldCell =
-        base::mac::ObjCCastStrict<AutofillEditCell>(cell);
+    TableViewTextEditCell* textFieldCell =
+        base::mac::ObjCCastStrict<TableViewTextEditCell>(cell);
     [textFieldCell.textField becomeFirstResponder];
   }
 }
diff --git a/ios/chrome/browser/ui/settings/clear_browsing_data/clear_browsing_data_table_view_controller.mm b/ios/chrome/browser/ui/settings/clear_browsing_data/clear_browsing_data_table_view_controller.mm
index c947fe5a..1c721cb9 100644
--- a/ios/chrome/browser/ui/settings/clear_browsing_data/clear_browsing_data_table_view_controller.mm
+++ b/ios/chrome/browser/ui/settings/clear_browsing_data/clear_browsing_data_table_view_controller.mm
@@ -138,7 +138,7 @@
   // Align cell separators with text label leading margin.
   [self.tableView
       setSeparatorInset:UIEdgeInsetsMake(0, kTableViewHorizontalSpacing, 0, 0)];
-
+  self.tableView.allowsMultipleSelection = YES;
   // Navigation controller configuration.
   self.title = l10n_util::GetNSString(IDS_IOS_CLEAR_BROWSING_DATA_TITLE);
   // Adds the "Done" button and hooks it up to |dismiss|.
@@ -160,7 +160,22 @@
 - (void)viewWillAppear:(BOOL)animated {
   [super viewWillAppear:animated];
   [self.dataManager restartCounters:BrowsingDataRemoveMask::REMOVE_ALL];
+
   if (IsNewClearBrowsingDataUIEnabled()) {
+    // Select those cells correspond to a checked item.
+    NSArray* dataTypeItems = [self.tableViewModel
+        itemsInSectionWithIdentifier:SectionIdentifierDataTypes];
+    for (TableViewClearBrowsingDataItem* dataTypeItem in dataTypeItems) {
+      DCHECK(
+          [dataTypeItem isKindOfClass:[TableViewClearBrowsingDataItem class]]);
+      if (dataTypeItem.checked) {
+        [self.tableView selectRowAtIndexPath:[self.tableViewModel
+                                                 indexPathForItem:dataTypeItem]
+                                    animated:NO
+                              scrollPosition:UITableViewScrollPositionNone];
+      }
+    }
+
     // Showing toolbar here because parent class hides toolbar in
     // viewWillDisappear:.
     self.navigationController.toolbarHidden = NO;
@@ -237,18 +252,41 @@
 
 - (void)tableView:(UITableView*)tableView
     didSelectRowAtIndexPath:(NSIndexPath*)indexPath {
+  if (!IsNewClearBrowsingDataUIEnabled()) {
+    [self tableView:tableView legacyDidSelectRowAtIndexPath:indexPath];
+  } else {
+    TableViewItem* item = [self.tableViewModel itemAtIndexPath:indexPath];
+    DCHECK(item);
+    switch (item.type) {
+      case ItemTypeTimeRange: {
+        UIViewController* controller =
+            [[TimeRangeSelectorTableViewController alloc]
+                initWithPrefs:self.browserState->GetPrefs()
+                     delegate:self.dataManager];
+        [self.tableView deselectRowAtIndexPath:indexPath animated:YES];
+        [self.navigationController pushViewController:controller animated:YES];
+        break;
+      }
+      case ItemTypeDataTypeBrowsingHistory:
+      case ItemTypeDataTypeCookiesSiteData:
+      case ItemTypeDataTypeCache:
+      case ItemTypeDataTypeSavedPasswords:
+      case ItemTypeDataTypeAutofill: {
+        [self updateItemAndReconfigureCellFor:item setChecked:YES];
+        break;
+      }
+      default:
+        break;
+    }
+  }
+}
+
+- (void)tableView:(UITableView*)tableView
+    legacyDidSelectRowAtIndexPath:(NSIndexPath*)indexPath {
   [self.tableView deselectRowAtIndexPath:indexPath animated:YES];
   TableViewItem* item = [self.tableViewModel itemAtIndexPath:indexPath];
   DCHECK(item);
   switch (item.type) {
-    case ItemTypeTimeRange: {
-      UIViewController* controller =
-          [[TimeRangeSelectorTableViewController alloc]
-              initWithPrefs:self.browserState->GetPrefs()
-                   delegate:self.dataManager];
-      [self.navigationController pushViewController:controller animated:YES];
-      break;
-    }
     case ItemTypeDataTypeBrowsingHistory:
     case ItemTypeDataTypeCookiesSiteData:
     case ItemTypeDataTypeCache:
@@ -262,11 +300,27 @@
       [self reconfigureCellsForItems:@[ clearBrowsingDataItem ]];
       break;
     }
-    case ItemTypeClearBrowsingDataButton:
-    case ItemTypeFooterGoogleAccount:
-    case ItemTypeFooterGoogleAccountAndMyActivity:
-    case ItemTypeFooterSavedSiteData:
-    case ItemTypeFooterClearSyncAndSavedSiteData:
+    default:
+      break;
+  }
+}
+
+- (void)tableView:(UITableView*)tableView
+    didDeselectRowAtIndexPath:(NSIndexPath*)indexPath {
+  if (!IsNewClearBrowsingDataUIEnabled()) {
+    return;
+  }
+  TableViewItem* item = [self.tableViewModel itemAtIndexPath:indexPath];
+  DCHECK(item);
+  switch (item.type) {
+    case ItemTypeDataTypeBrowsingHistory:
+    case ItemTypeDataTypeCookiesSiteData:
+    case ItemTypeDataTypeCache:
+    case ItemTypeDataTypeSavedPasswords:
+    case ItemTypeDataTypeAutofill: {
+      [self updateItemAndReconfigureCellFor:item setChecked:NO];
+      break;
+    }
     default:
       break;
   }
@@ -296,6 +350,20 @@
   // thus the cell height needs to adapt accordingly.
   [self reloadCellsForItems:@[ item ]
            withRowAnimation:UITableViewRowAnimationAutomatic];
+
+  // Restore a cell's seleted state potentially cleared by the above reload
+  // method.
+  if (IsNewClearBrowsingDataUIEnabled() &&
+      [item isKindOfClass:[TableViewClearBrowsingDataItem class]]) {
+    TableViewClearBrowsingDataItem* dataTypeItem =
+        base::mac::ObjCCastStrict<TableViewClearBrowsingDataItem>(item);
+    if (dataTypeItem.checked) {
+      [self.tableView selectRowAtIndexPath:[self.tableViewModel
+                                               indexPathForItem:dataTypeItem]
+                                  animated:NO
+                            scrollPosition:UITableViewScrollPositionNone];
+    }
+  }
 }
 
 - (void)removeBrowsingDataForBrowserState:(ios::ChromeBrowserState*)browserState
@@ -404,4 +472,21 @@
   [self.actionSheetCoordinator start];
 }
 
+// Helper of |tableView:didSelectRowAtIndexPath:| and
+// |tableView:didDeselectRowAtIndexPath:| for browsing data items.
+// Sets |item|'s |checked| to |flag|, which depends on whether it's a selection
+// or a deselection, then performs updates accordingly.
+- (void)updateItemAndReconfigureCellFor:(TableViewItem*)item
+                             setChecked:(BOOL)flag {
+  if (![item isKindOfClass:[TableViewClearBrowsingDataItem class]]) {
+    return;
+  }
+  TableViewClearBrowsingDataItem* clearBrowsingDataItem =
+      base::mac::ObjCCastStrict<TableViewClearBrowsingDataItem>(item);
+  clearBrowsingDataItem.checked = flag;
+  self.browserState->GetPrefs()->SetBoolean(clearBrowsingDataItem.prefName,
+                                            clearBrowsingDataItem.checked);
+  [self reconfigureCellsForItems:@[ clearBrowsingDataItem ]];
+}
+
 @end
diff --git a/ios/chrome/browser/ui/settings/privacy_table_view_controller.mm b/ios/chrome/browser/ui/settings/privacy_table_view_controller.mm
index 9bb1833..097f7b7 100644
--- a/ios/chrome/browser/ui/settings/privacy_table_view_controller.mm
+++ b/ios/chrome/browser/ui/settings/privacy_table_view_controller.mm
@@ -20,6 +20,7 @@
 #include "components/unified_consent/feature.h"
 #include "ios/chrome/browser/application_context.h"
 #include "ios/chrome/browser/browser_state/chrome_browser_state.h"
+#include "ios/chrome/browser/browsing_data/browsing_data_features.h"
 #include "ios/chrome/browser/chrome_url_constants.h"
 #include "ios/chrome/browser/pref_names.h"
 #include "ios/chrome/browser/system_flags.h"
@@ -383,7 +384,7 @@
       }
       break;
     case ItemTypeClearBrowsingDataClear:
-      if (base::FeatureList::IsEnabled(kSettingsRefresh)) {
+      if (IsNewClearBrowsingDataUIEnabled()) {
         ClearBrowsingDataTableViewController* clearBrowsingDataViewController =
             [[ClearBrowsingDataTableViewController alloc]
                 initWithBrowserState:_browserState];
diff --git a/ios/chrome/browser/ui/settings/table_cell_catalog_view_controller.mm b/ios/chrome/browser/ui/settings/table_cell_catalog_view_controller.mm
index 4ab6324..a4e93cb 100644
--- a/ios/chrome/browser/ui/settings/table_cell_catalog_view_controller.mm
+++ b/ios/chrome/browser/ui/settings/table_cell_catalog_view_controller.mm
@@ -54,6 +54,7 @@
   ItemTypeURLNoMetadata,
   ItemTypeTextAccessoryImage,
   ItemTypeTextAccessoryNoImage,
+  ItemTypeTextEditItem,
   ItemTypeURLWithTimestamp,
   ItemTypeURLWithSize,
   ItemTypeURLWithSupplementalText,
@@ -66,7 +67,6 @@
   ItemTypeSettingsSwitch2,
   ItemTypeSyncSwitch,
   ItemTypeSettingsSyncError,
-  ItemTypeAutofillEditItem,
   ItemTypeAutofillData,
   ItemTypeAccount,
 };
@@ -204,6 +204,32 @@
   [model addItem:detailIconItemBothLong
       toSectionWithIdentifier:SectionIdentifierText];
 
+  TableViewTextEditItem* textEditItem =
+      [[TableViewTextEditItem alloc] initWithType:ItemTypeTextEditItem];
+  textEditItem.textFieldName = @"Edit Text Item";
+  textEditItem.textFieldValue = @" with no icons";
+  textEditItem.hideEditIcon = YES;
+  textEditItem.textFieldEnabled = YES;
+  [model addItem:textEditItem toSectionWithIdentifier:SectionIdentifierText];
+
+  TableViewTextEditItem* textEditItemEditIcon =
+      [[TableViewTextEditItem alloc] initWithType:ItemTypeTextEditItem];
+  textEditItemEditIcon.textFieldName = @"Edit Text Item";
+  textEditItemEditIcon.textFieldValue = @" with edit icon";
+  textEditItemEditIcon.textFieldEnabled = YES;
+  [model addItem:textEditItemEditIcon
+      toSectionWithIdentifier:SectionIdentifierText];
+
+  TableViewTextEditItem* textEditItemBothIcons =
+      [[TableViewTextEditItem alloc] initWithType:ItemTypeTextEditItem];
+  textEditItemBothIcons.textFieldName = @"Edit Text Item";
+  textEditItemBothIcons.textFieldValue = @" with edit and custom icons";
+  textEditItemBothIcons.identifyingIcon =
+      [UIImage imageNamed:@"table_view_cell_check_mark"];
+  textEditItemBothIcons.textFieldEnabled = YES;
+  [model addItem:textEditItemBothIcons
+      toSectionWithIdentifier:SectionIdentifierText];
+
   // SectionIdentifierSettings.
   TableViewTextHeaderFooterItem* settingsHeader =
       [[TableViewTextHeaderFooterItem alloc] initWithType:ItemTypeTextHeader];
@@ -265,15 +291,6 @@
   [model setHeader:autofillHeader
       forSectionWithIdentifier:SectionIdentifierAutofill];
 
-  AutofillEditItem* autofillEditItem =
-      [[AutofillEditItem alloc] initWithType:ItemTypeAutofillEditItem];
-  autofillEditItem.textFieldName = @"Autofill field";
-  autofillEditItem.textFieldValue = @" with a value";
-  autofillEditItem.identifyingIcon =
-      [UIImage imageNamed:@"table_view_cell_check_mark"];
-  [model addItem:autofillEditItem
-      toSectionWithIdentifier:SectionIdentifierAutofill];
-
   AutofillDataItem* autofillItemWithMainLeading =
       [[AutofillDataItem alloc] initWithType:ItemTypeAutofillData];
   autofillItemWithMainLeading.text = @"Main Text";
diff --git a/ios/chrome/browser/ui/table_view/cells/BUILD.gn b/ios/chrome/browser/ui/table_view/cells/BUILD.gn
index a7f463a..a559b47 100644
--- a/ios/chrome/browser/ui/table_view/cells/BUILD.gn
+++ b/ios/chrome/browser/ui/table_view/cells/BUILD.gn
@@ -26,6 +26,8 @@
     "table_view_link_header_footer_item.mm",
     "table_view_text_button_item.h",
     "table_view_text_button_item.mm",
+    "table_view_text_edit_item.h",
+    "table_view_text_edit_item.mm",
     "table_view_text_header_footer_item.h",
     "table_view_text_header_footer_item.mm",
     "table_view_text_item.h",
@@ -40,6 +42,7 @@
 
   deps = [
     "resources:table_view_cell_chevron",
+    "resources:table_view_cell_edit_icon",
     "resources:table_view_cell_favicon_background",
     "//base",
     "//base:i18n",
@@ -68,6 +71,7 @@
     "table_view_image_item_unittest.mm",
     "table_view_item_unittest.mm",
     "table_view_text_button_item_unittest.mm",
+    "table_view_text_edit_item_unittest.mm",
     "table_view_text_header_footer_item_unittest.mm",
     "table_view_text_item_unittest.mm",
     "table_view_url_item_unittest.mm",
diff --git a/ios/chrome/browser/ui/table_view/cells/resources/BUILD.gn b/ios/chrome/browser/ui/table_view/cells/resources/BUILD.gn
index 55294924..b98df7d1 100644
--- a/ios/chrome/browser/ui/table_view/cells/resources/BUILD.gn
+++ b/ios/chrome/browser/ui/table_view/cells/resources/BUILD.gn
@@ -30,3 +30,11 @@
     "table_view_cell_favicon_background.imageset/table_view_cell_favicon_background@3x.png",
   ]
 }
+
+imageset("table_view_cell_edit_icon") {
+  sources = [
+    "table_view_cell_edit_icon.imageset/Contents.json",
+    "table_view_cell_edit_icon.imageset/table_view_cell_edit_icon@2x.png",
+    "table_view_cell_edit_icon.imageset/table_view_cell_edit_icon@3x.png",
+  ]
+}
diff --git a/ios/chrome/browser/ui/autofill/cells/resources/autofill_edit_item_icon.imageset/Contents.json b/ios/chrome/browser/ui/table_view/cells/resources/table_view_cell_edit_icon.imageset/Contents.json
similarity index 74%
rename from ios/chrome/browser/ui/autofill/cells/resources/autofill_edit_item_icon.imageset/Contents.json
rename to ios/chrome/browser/ui/table_view/cells/resources/table_view_cell_edit_icon.imageset/Contents.json
index d275f8b..894124ae 100644
--- a/ios/chrome/browser/ui/autofill/cells/resources/autofill_edit_item_icon.imageset/Contents.json
+++ b/ios/chrome/browser/ui/table_view/cells/resources/table_view_cell_edit_icon.imageset/Contents.json
@@ -7,12 +7,12 @@
         {
             "idiom": "universal",
             "scale": "2x",
-            "filename": "autofill_edit_item_icon@2x.png"
+            "filename": "table_view_cell_edit_icon@2x.png"
         },
         {
             "idiom": "universal",
             "scale": "3x",
-            "filename": "autofill_edit_item_icon@3x.png"
+            "filename": "table_view_cell_edit_icon@3x.png"
         }
     ],
     "info": {
diff --git a/ios/chrome/browser/ui/autofill/cells/resources/autofill_edit_item_icon.imageset/autofill_edit_item_icon@2x.png b/ios/chrome/browser/ui/table_view/cells/resources/table_view_cell_edit_icon.imageset/table_view_cell_edit_icon@2x.png
similarity index 100%
rename from ios/chrome/browser/ui/autofill/cells/resources/autofill_edit_item_icon.imageset/autofill_edit_item_icon@2x.png
rename to ios/chrome/browser/ui/table_view/cells/resources/table_view_cell_edit_icon.imageset/table_view_cell_edit_icon@2x.png
Binary files differ
diff --git a/ios/chrome/browser/ui/autofill/cells/resources/autofill_edit_item_icon.imageset/autofill_edit_item_icon@3x.png b/ios/chrome/browser/ui/table_view/cells/resources/table_view_cell_edit_icon.imageset/table_view_cell_edit_icon@3x.png
similarity index 100%
rename from ios/chrome/browser/ui/autofill/cells/resources/autofill_edit_item_icon.imageset/autofill_edit_item_icon@3x.png
rename to ios/chrome/browser/ui/table_view/cells/resources/table_view_cell_edit_icon.imageset/table_view_cell_edit_icon@3x.png
Binary files differ
diff --git a/ios/chrome/browser/ui/table_view/cells/table_view_text_edit_item.h b/ios/chrome/browser/ui/table_view/cells/table_view_text_edit_item.h
new file mode 100644
index 0000000..c4a06e5
--- /dev/null
+++ b/ios/chrome/browser/ui/table_view/cells/table_view_text_edit_item.h
@@ -0,0 +1,66 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef IOS_CHROME_BROWSER_UI_TABLE_VIEW_CELLS_TABLE_VIEW_TEXT_EDIT_ITEM_H_
+#define IOS_CHROME_BROWSER_UI_TABLE_VIEW_CELLS_TABLE_VIEW_TEXT_EDIT_ITEM_H_
+
+#import <UIKit/UIKit.h>
+
+#import "ios/chrome/browser/ui/table_view/cells/table_view_item.h"
+
+// Item to represent and configure an TableViewTextEditItem. It features a label
+// and a text field.
+@interface TableViewTextEditItem : TableViewItem
+
+// The name of the text field.
+@property(nonatomic, copy) NSString* textFieldName;
+
+// The value of the text field.
+@property(nonatomic, copy) NSString* textFieldValue;
+
+// An icon identifying the text field or its current value, if any.
+@property(nonatomic, copy) UIImage* identifyingIcon;
+
+// Whether to hide or display the trailing edit icon.
+@property(nonatomic, assign) BOOL hideEditIcon;
+
+// Whether this field is required. If YES, an "*" is appended to the name of the
+// text field to indicate that the field is required. It is also used for
+// validation purposes.
+@property(nonatomic, getter=isRequired) BOOL required;
+
+// Whether the text field is enabled for editing.
+@property(nonatomic, getter=isTextFieldEnabled) BOOL textFieldEnabled;
+
+// Controls the display of the return key when the keyboard is displaying.
+@property(nonatomic, assign) UIReturnKeyType returnKeyType;
+
+// Keyboard type to be displayed when the text field becomes first responder.
+@property(nonatomic, assign) UIKeyboardType keyboardType;
+
+// Controls autocapitalization behavior of the text field.
+@property(nonatomic, assign)
+    UITextAutocapitalizationType autoCapitalizationType;
+
+@end
+
+// TableViewTextEditCell implements an TableViewCell subclass containing a label
+// and a text field.
+@interface TableViewTextEditCell : TableViewCell
+
+// Label at the leading edge of the cell. It displays the item's textFieldName.
+@property(nonatomic, strong) UILabel* textLabel;
+
+// Text field at the trailing edge of the cell. It displays the item's
+// |textFieldValue|.
+@property(nonatomic, readonly, strong) UITextField* textField;
+
+// Whether the icon showing that the cell is editable should be displayed.
+@property(nonatomic, assign) BOOL editIconDisplayed;
+
+- (void)setIdentifyingIcon:(UIImage*)icon;
+
+@end
+
+#endif  // IOS_CHROME_BROWSER_UI_TABLE_VIEW_CELLS_TABLE_VIEW_TEXT_EDIT_ITEM_H_
diff --git a/ios/chrome/browser/ui/table_view/cells/table_view_text_edit_item.mm b/ios/chrome/browser/ui/table_view/cells/table_view_text_edit_item.mm
new file mode 100644
index 0000000..b600056
--- /dev/null
+++ b/ios/chrome/browser/ui/table_view/cells/table_view_text_edit_item.mm
@@ -0,0 +1,323 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#import "ios/chrome/browser/ui/table_view/cells/table_view_text_edit_item.h"
+
+#import "ios/chrome/browser/ui/table_view/cells/table_view_cells_constants.h"
+#import "ios/chrome/browser/ui/table_view/chrome_table_view_styler.h"
+#import "ios/chrome/browser/ui/util/rtl_geometry.h"
+#import "ios/chrome/browser/ui/util/uikit_ui_util.h"
+#import "ios/chrome/common/ui_util/constraints_ui_util.h"
+
+#if !defined(__has_feature) || !__has_feature(objc_arc)
+#error "This file requires ARC support."
+#endif
+
+namespace {
+// Minimum gap between the label and the text field.
+const CGFloat kLabelAndFieldGap = 5;
+// Height/width of the edit icon.
+const CGFloat kEditIconLength = 18;
+}  // namespace
+
+@implementation TableViewTextEditItem
+
+- (instancetype)initWithType:(NSInteger)type {
+  self = [super initWithType:type];
+  if (self) {
+    self.cellClass = [TableViewTextEditCell class];
+    _returnKeyType = UIReturnKeyNext;
+    _keyboardType = UIKeyboardTypeDefault;
+    _autoCapitalizationType = UITextAutocapitalizationTypeWords;
+  }
+  return self;
+}
+
+#pragma mark TableViewItem
+
+- (void)configureCell:(TableViewTextEditCell*)cell
+           withStyler:(ChromeTableViewStyler*)styler {
+  [super configureCell:cell withStyler:styler];
+
+  NSString* textLabelFormat = self.required ? @"%@*" : @"%@";
+  cell.textLabel.text =
+      [NSString stringWithFormat:textLabelFormat, self.textFieldName];
+  cell.textField.text = self.textFieldValue;
+  if (self.textFieldName.length) {
+    cell.textField.accessibilityIdentifier =
+        [NSString stringWithFormat:@"%@_textField", self.textFieldName];
+  }
+  if (styler.cellBackgroundColor) {
+    cell.textLabel.backgroundColor = styler.cellBackgroundColor;
+    cell.textField.backgroundColor = styler.cellBackgroundColor;
+  } else {
+    cell.textLabel.backgroundColor = styler.tableViewBackgroundColor;
+    cell.textField.backgroundColor = styler.tableViewBackgroundColor;
+  }
+  cell.textField.enabled = self.textFieldEnabled;
+  if (self.hideEditIcon) {
+    cell.textField.textColor =
+        self.textFieldEnabled
+            ? UIColorFromRGB(kTableViewTextLabelColorBlue)
+            : UIColorFromRGB(kTableViewSecondaryLabelLightGrayTextColor);
+  } else {
+    cell.textField.textColor =
+        UIColorFromRGB(kTableViewSecondaryLabelLightGrayTextColor);
+    cell.editIconDisplayed = self.textFieldEnabled;
+  }
+  [cell.textField addTarget:self
+                     action:@selector(textFieldChanged:)
+           forControlEvents:UIControlEventEditingChanged];
+  cell.textField.returnKeyType = self.returnKeyType;
+  cell.textField.keyboardType = self.keyboardType;
+  cell.textField.autocapitalizationType = self.autoCapitalizationType;
+  [cell setIdentifyingIcon:self.identifyingIcon];
+}
+
+#pragma mark Actions
+
+- (void)textFieldChanged:(UITextField*)textField {
+  self.textFieldValue = textField.text;
+}
+
+@end
+
+#pragma mark - TableViewTextEditCell
+
+@interface TableViewTextEditCell ()
+
+@property(nonatomic, strong) NSLayoutConstraint* iconHeightConstraint;
+@property(nonatomic, strong) NSLayoutConstraint* iconWidthConstraint;
+@property(nonatomic, strong) NSLayoutConstraint* textFieldTrailingConstraint;
+@property(nonatomic, strong) NSLayoutConstraint* textLabelTrailingConstraint;
+
+@property(nonatomic, strong) NSLayoutConstraint* editIconHeightConstraint;
+@property(nonatomic, strong) NSLayoutConstraint* iconTrailingConstraint;
+
+// When they are activated, the label and the text field are on one line.
+// They conflict with the |accessibilityConstraints|.
+@property(nonatomic, strong) NSArray<NSLayoutConstraint*>* standardConstraints;
+// When they are activated, the label is on one line, the text field is on
+// another line. They conflict with the |standardConstraints|.
+@property(nonatomic, strong)
+    NSArray<NSLayoutConstraint*>* accessibilityConstraints;
+
+// UIImageView containing the icon identifying |textField| or its current value.
+@property(nonatomic, readonly, strong) UIImageView* identifyingIconView;
+
+// UIImageView containing the icon indicating that |textField| is editable.
+@property(nonatomic, strong) UIImageView* editIconView;
+
+@end
+
+@implementation TableViewTextEditCell
+
+@synthesize textLabel = _textLabel;
+
+- (instancetype)initWithStyle:(UITableViewCellStyle)style
+              reuseIdentifier:(NSString*)reuseIdentifier {
+  self = [super initWithStyle:style reuseIdentifier:reuseIdentifier];
+  if (self) {
+    self.isAccessibilityElement = YES;
+    UIView* contentView = self.contentView;
+
+    _textLabel = [[UILabel alloc] init];
+    _textLabel.translatesAutoresizingMaskIntoConstraints = NO;
+    [_textLabel setContentHuggingPriority:UILayoutPriorityDefaultHigh
+                                  forAxis:UILayoutConstraintAxisHorizontal];
+    _textLabel.font = [UIFont preferredFontForTextStyle:UIFontTextStyleBody];
+    _textLabel.adjustsFontForContentSizeCategory = YES;
+    [contentView addSubview:_textLabel];
+
+    _textField = [[UITextField alloc] init];
+    _textField.translatesAutoresizingMaskIntoConstraints = NO;
+    _textField.font = [UIFont preferredFontForTextStyle:UIFontTextStyleBody];
+    _textField.adjustsFontForContentSizeCategory = YES;
+    [_textField
+        setContentCompressionResistancePriority:UILayoutPriorityDefaultLow
+                                        forAxis:
+                                            UILayoutConstraintAxisHorizontal];
+    [contentView addSubview:_textField];
+
+    _textField.autocorrectionType = UITextAutocorrectionTypeNo;
+    _textField.clearButtonMode = UITextFieldViewModeWhileEditing;
+    _textField.contentVerticalAlignment =
+        UIControlContentVerticalAlignmentCenter;
+
+    // Trailing con.
+    _identifyingIconView = [[UIImageView alloc] initWithFrame:CGRectZero];
+    _identifyingIconView.translatesAutoresizingMaskIntoConstraints = NO;
+    [contentView addSubview:_identifyingIconView];
+
+    // Edit icon.
+    UIImage* editImage = [[UIImage imageNamed:@"table_view_cell_edit_icon"]
+        imageWithRenderingMode:UIImageRenderingModeAlwaysTemplate];
+    _editIconView = [[UIImageView alloc] initWithImage:editImage];
+    _editIconView.tintColor =
+        UIColorFromRGB(kTableViewSecondaryLabelLightGrayTextColor);
+    _editIconView.translatesAutoresizingMaskIntoConstraints = NO;
+    [contentView addSubview:_editIconView];
+
+    // Set up the icons size constraints. They are activated here and updated in
+    // layoutSubviews.
+    _iconHeightConstraint =
+        [_identifyingIconView.heightAnchor constraintEqualToConstant:0];
+    _iconWidthConstraint =
+        [_identifyingIconView.widthAnchor constraintEqualToConstant:0];
+    _editIconHeightConstraint =
+        [_editIconView.heightAnchor constraintEqualToConstant:0];
+
+    _textFieldTrailingConstraint = [_textField.trailingAnchor
+        constraintEqualToAnchor:_editIconView.leadingAnchor];
+    _textLabelTrailingConstraint = [_textLabel.trailingAnchor
+        constraintEqualToAnchor:_editIconView.leadingAnchor];
+    _iconTrailingConstraint = [_editIconView.trailingAnchor
+        constraintEqualToAnchor:_identifyingIconView.leadingAnchor];
+
+    _standardConstraints = @[
+      [_textField.firstBaselineAnchor
+          constraintEqualToAnchor:_textLabel.firstBaselineAnchor],
+      [_textField.leadingAnchor
+          constraintEqualToAnchor:_textLabel.trailingAnchor
+                         constant:kLabelAndFieldGap],
+    ];
+
+    _accessibilityConstraints = @[
+      [_textField.topAnchor constraintEqualToAnchor:_textLabel.bottomAnchor
+                                           constant:kTableViewVerticalSpacing],
+      [_textField.leadingAnchor
+          constraintEqualToAnchor:contentView.leadingAnchor
+                         constant:kTableViewHorizontalSpacing],
+      _textLabelTrailingConstraint,
+    ];
+
+    // Set up the constraints.
+    [NSLayoutConstraint activateConstraints:@[
+      [_textLabel.leadingAnchor
+          constraintEqualToAnchor:contentView.leadingAnchor
+                         constant:kTableViewHorizontalSpacing],
+      _textFieldTrailingConstraint,
+      [_identifyingIconView.trailingAnchor
+          constraintEqualToAnchor:contentView.trailingAnchor
+                         constant:-kTableViewHorizontalSpacing],
+      [_identifyingIconView.centerYAnchor
+          constraintEqualToAnchor:contentView.centerYAnchor],
+      [_editIconView.centerYAnchor
+          constraintEqualToAnchor:contentView.centerYAnchor],
+      _iconHeightConstraint,
+      _iconWidthConstraint,
+      _iconTrailingConstraint,
+      _editIconHeightConstraint,
+      [_editIconView.widthAnchor
+          constraintEqualToAnchor:_editIconView.heightAnchor],
+    ]];
+    AddOptionalVerticalPadding(contentView, _textLabel,
+                               kTableViewOneLabelCellVerticalSpacing);
+    AddOptionalVerticalPadding(contentView, _textField,
+                               kTableViewOneLabelCellVerticalSpacing);
+
+    [self updateForAccessibilityContentSizeCategory:
+              UIContentSizeCategoryIsAccessibilityCategory(
+                  self.traitCollection.preferredContentSizeCategory)];
+  }
+  return self;
+}
+
+#pragma mark Public
+
+- (void)setEditIconDisplayed:(BOOL)editIconDisplayed {
+  if (editIconDisplayed == _editIconDisplayed)
+    return;
+
+  _editIconDisplayed = editIconDisplayed;
+  self.editIconView.hidden = !editIconDisplayed;
+  if (editIconDisplayed) {
+    self.textFieldTrailingConstraint.constant = -kLabelAndFieldGap;
+    self.textLabelTrailingConstraint.constant = -kLabelAndFieldGap;
+
+    _editIconHeightConstraint.constant = kEditIconLength;
+  } else {
+    self.textFieldTrailingConstraint.constant = 0;
+    self.textLabelTrailingConstraint.constant = 0;
+
+    _editIconHeightConstraint.constant = 0;
+  }
+}
+
+- (void)setIdentifyingIcon:(UIImage*)icon {
+  self.identifyingIconView.image = icon;
+  if (icon) {
+    self.iconTrailingConstraint.constant = -kLabelAndFieldGap;
+
+    // Set the size constraints of the icon view to the dimensions of the image.
+    self.iconHeightConstraint.constant = icon.size.height;
+    self.iconWidthConstraint.constant = icon.size.width;
+  } else {
+    self.iconTrailingConstraint.constant = 0;
+    self.iconHeightConstraint.constant = 0;
+    self.iconWidthConstraint.constant = 0;
+  }
+}
+
+- (void)traitCollectionDidChange:(UITraitCollection*)previousTraitCollection {
+  [super traitCollectionDidChange:previousTraitCollection];
+  BOOL isCurrentCategoryAccessibility =
+      UIContentSizeCategoryIsAccessibilityCategory(
+          self.traitCollection.preferredContentSizeCategory);
+  if (isCurrentCategoryAccessibility !=
+      UIContentSizeCategoryIsAccessibilityCategory(
+          previousTraitCollection.preferredContentSizeCategory)) {
+    [self updateForAccessibilityContentSizeCategory:
+              isCurrentCategoryAccessibility];
+  }
+}
+
+#pragma mark - UITableViewCell
+
+- (void)prepareForReuse {
+  [super prepareForReuse];
+  self.textLabel.text = nil;
+  self.textField.text = nil;
+  self.textField.returnKeyType = UIReturnKeyNext;
+  self.textField.keyboardType = UIKeyboardTypeDefault;
+  self.textField.autocapitalizationType = UITextAutocapitalizationTypeWords;
+  self.textField.autocorrectionType = UITextAutocorrectionTypeNo;
+  self.textField.clearButtonMode = UITextFieldViewModeWhileEditing;
+  self.textField.accessibilityIdentifier = nil;
+  self.textField.enabled = NO;
+  self.textField.delegate = nil;
+  [self.textField removeTarget:nil
+                        action:nil
+              forControlEvents:UIControlEventAllEvents];
+  self.identifyingIconView.image = nil;
+}
+
+#pragma mark Accessibility
+
+- (NSString*)accessibilityLabel {
+  return [NSString
+      stringWithFormat:@"%@, %@", self.textLabel.text, self.textField.text];
+}
+
+#pragma mark Private
+
+// Updates the cell such as it is layouted correctly with regard to the
+// preferred content size category, if it is an
+// |accessibilityContentSizeCategory| or not.
+- (void)updateForAccessibilityContentSizeCategory:
+    (BOOL)accessibilityContentSizeCategory {
+  if (accessibilityContentSizeCategory) {
+    [NSLayoutConstraint deactivateConstraints:_standardConstraints];
+    [NSLayoutConstraint activateConstraints:_accessibilityConstraints];
+    _textField.textAlignment =
+        UseRTLLayout() ? NSTextAlignmentRight : NSTextAlignmentLeft;
+  } else {
+    [NSLayoutConstraint deactivateConstraints:_accessibilityConstraints];
+    [NSLayoutConstraint activateConstraints:_standardConstraints];
+    _textField.textAlignment =
+        UseRTLLayout() ? NSTextAlignmentLeft : NSTextAlignmentRight;
+  }
+}
+
+@end
diff --git a/ios/chrome/browser/ui/table_view/cells/table_view_text_edit_item_unittest.mm b/ios/chrome/browser/ui/table_view/cells/table_view_text_edit_item_unittest.mm
new file mode 100644
index 0000000..384cfe0a
--- /dev/null
+++ b/ios/chrome/browser/ui/table_view/cells/table_view_text_edit_item_unittest.mm
@@ -0,0 +1,42 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#import "ios/chrome/browser/ui/table_view/cells/table_view_text_edit_item.h"
+
+#import "ios/chrome/browser/ui/table_view/chrome_table_view_styler.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#import "testing/gtest_mac.h"
+#include "testing/platform_test.h"
+
+#if !defined(__has_feature) || !__has_feature(objc_arc)
+#error "This file requires ARC support."
+#endif
+
+using TableViewTextEditItemTest = PlatformTest;
+
+// Tests that the label and text field are set properly after a call to
+// |configureCell:|.
+TEST_F(TableViewTextEditItemTest, ConfigureCell) {
+  TableViewTextEditItem* item = [[TableViewTextEditItem alloc] initWithType:0];
+  NSString* name = @"Name";
+  NSString* value = @"Value";
+  BOOL enabled = NO;
+
+  item.textFieldName = name;
+  item.textFieldValue = value;
+  item.textFieldEnabled = enabled;
+
+  id cell = [[[item cellClass] alloc] init];
+  ASSERT_TRUE([cell isMemberOfClass:[TableViewTextEditCell class]]);
+
+  TableViewTextEditCell* textEditCell = cell;
+  EXPECT_EQ(0U, textEditCell.textLabel.text.length);
+  EXPECT_EQ(0U, textEditCell.textField.text.length);
+  EXPECT_TRUE(textEditCell.textField.enabled);
+
+  [item configureCell:cell withStyler:[[ChromeTableViewStyler alloc] init]];
+  EXPECT_NSEQ(name, textEditCell.textLabel.text);
+  EXPECT_NSEQ(value, textEditCell.textField.text);
+  EXPECT_FALSE(textEditCell.textField.enabled);
+}
diff --git a/ios/chrome/browser/ui/webui/BUILD.gn b/ios/chrome/browser/ui/webui/BUILD.gn
index 02a25b712..188f0f4 100644
--- a/ios/chrome/browser/ui/webui/BUILD.gn
+++ b/ios/chrome/browser/ui/webui/BUILD.gn
@@ -100,6 +100,7 @@
     "//ios/chrome/browser/ui/webui/gcm",
     "//ios/chrome/browser/ui/webui/net_export",
     "//ios/chrome/browser/ui/webui/sync_internals",
+    "//ios/chrome/browser/ui/webui/translate_internals",
     "//ios/web",
     "//services/identity/public/cpp",
     "//url",
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 303ee10..5a4e552 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
@@ -23,6 +23,7 @@
 #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"
@@ -89,6 +90,8 @@
     return &NewWebUIIOS<SignInInternalsUIIOS>;
   if (url.host_piece() == kChromeUISuggestionsHost)
     return &NewWebUIIOS<suggestions::SuggestionsUI>;
+  if (url.host_piece() == kChromeUITranslateInternalsHost)
+    return &NewWebUIIOS<TranslateInternalsUI>;
   if (url_host == kChromeUIURLKeyedMetricsHost)
     return &NewWebUIIOS<UkmInternalsUI>;
   if (url_host == kChromeUIUserActionsHost)
diff --git a/ios/chrome/browser/ui/webui/translate_internals/BUILD.gn b/ios/chrome/browser/ui/webui/translate_internals/BUILD.gn
new file mode 100644
index 0000000..0771ed4
--- /dev/null
+++ b/ios/chrome/browser/ui/webui/translate_internals/BUILD.gn
@@ -0,0 +1,25 @@
+# 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.
+
+source_set("translate_internals") {
+  configs += [ "//build/config/compiler:enable_arc" ]
+  sources = [
+    "ios_translate_internals_handler.h",
+    "ios_translate_internals_handler.mm",
+    "translate_internals_ui.h",
+    "translate_internals_ui.mm",
+  ]
+  deps = [
+    "//components/language/ios/browser",
+    "//components/translate/core/common",
+    "//components/translate/translate_internals",
+    "//ios/chrome/app/resources:ios_resources",
+    "//ios/chrome/browser",
+    "//ios/chrome/browser/browser_state",
+    "//ios/chrome/browser/tabs",
+    "//ios/chrome/browser/translate",
+    "//ios/chrome/browser/web_state_list",
+    "//ios/web/public",
+  ]
+}
diff --git a/ios/chrome/browser/ui/webui/translate_internals/ios_translate_internals_handler.h b/ios/chrome/browser/ui/webui/translate_internals/ios_translate_internals_handler.h
new file mode 100644
index 0000000..27dffad
--- /dev/null
+++ b/ios/chrome/browser/ui/webui/translate_internals/ios_translate_internals_handler.h
@@ -0,0 +1,80 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef IOS_CHROME_BROWSER_UI_WEBUI_TRANSLATE_INTERNALS_IOS_TRANSLATE_INTERNALS_HANDLER_H_
+#define IOS_CHROME_BROWSER_UI_WEBUI_TRANSLATE_INTERNALS_IOS_TRANSLATE_INTERNALS_HANDLER_H_
+
+#include <string>
+
+#include "base/macros.h"
+#include "base/scoped_observer.h"
+#include "components/language/ios/browser/ios_language_detection_tab_helper.h"
+#include "components/translate/translate_internals/translate_internals_handler.h"
+#import "ios/chrome/browser/web_state_list/web_state_list_observer.h"
+#include "ios/web/public/webui/web_ui_ios_message_handler.h"
+
+namespace web {
+class WebState;
+}  // namespace web
+
+// The handler for JavaScript messages for chrome://translate-internals.
+class IOSTranslateInternalsHandler
+    : public translate::TranslateInternalsHandler,
+      public web::WebUIIOSMessageHandler,
+      public WebStateListObserver,
+      public language::IOSLanguageDetectionTabHelper::Observer {
+ public:
+  IOSTranslateInternalsHandler();
+  ~IOSTranslateInternalsHandler() override;
+
+  // translate::TranslateInternalsHandler.
+  translate::TranslateClient* GetTranslateClient() override;
+  variations::VariationsService* GetVariationsService() override;
+  void RegisterMessageCallback(const std::string& message,
+                               const MessageCallback& callback) override;
+  void CallJavascriptFunction(
+      const std::string& function_name,
+      const std::vector<const base::Value*>& args) override;
+
+  // web::WebUIIOSMessageHandler.
+  void RegisterMessages() override;
+
+  // WebStateListObserver.
+  void WebStateInsertedAt(WebStateList* web_state_list,
+                          web::WebState* web_state,
+                          int index,
+                          bool activating) override;
+  void WebStateReplacedAt(WebStateList* web_state_list,
+                          web::WebState* old_web_state,
+                          web::WebState* new_web_state,
+                          int index) override;
+  void WebStateDetachedAt(WebStateList* web_state_list,
+                          web::WebState* web_state,
+                          int index) override;
+
+  // language::IOSLanguageDetectionTabHelper::Observer
+  void OnLanguageDetermined(
+      const translate::LanguageDetectionDetails& details) override;
+  void IOSLanguageDetectionTabHelperWasDestroyed(
+      language::IOSLanguageDetectionTabHelper* tab_helper) override;
+
+ private:
+  // Adds this instance as an observer of the IOSLanguageDetectionTabHelper
+  // associated with |web_state|.
+  void AddLanguageDetectionObserverForWebState(web::WebState* web_state);
+  // Removes this instance as an observer of the IOSLanguageDetectionTabHelper
+  // associated with |web_state|.
+  void RemoveLanguageDetectionObserverForWebState(web::WebState* web_state);
+
+  std::unique_ptr<ScopedObserver<WebStateList, WebStateListObserver>>
+      scoped_web_state_list_observer_;
+  std::unique_ptr<
+      ScopedObserver<language::IOSLanguageDetectionTabHelper,
+                     language::IOSLanguageDetectionTabHelper::Observer>>
+      scoped_tab_helper_observer_;
+
+  DISALLOW_COPY_AND_ASSIGN(IOSTranslateInternalsHandler);
+};
+
+#endif  // IOS_CHROME_BROWSER_UI_WEBUI_TRANSLATE_INTERNALS_IOS_TRANSLATE_INTERNALS_HANDLER_H_
diff --git a/ios/chrome/browser/ui/webui/translate_internals/ios_translate_internals_handler.mm b/ios/chrome/browser/ui/webui/translate_internals/ios_translate_internals_handler.mm
new file mode 100644
index 0000000..98b6694
--- /dev/null
+++ b/ios/chrome/browser/ui/webui/translate_internals/ios_translate_internals_handler.mm
@@ -0,0 +1,126 @@
+// 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 "ios/chrome/browser/ui/webui/translate_internals/ios_translate_internals_handler.h"
+
+#include "components/translate/core/common/language_detection_details.h"
+#include "ios/chrome/browser/application_context.h"
+#include "ios/chrome/browser/browser_state/chrome_browser_state.h"
+#import "ios/chrome/browser/tabs/tab_model.h"
+#import "ios/chrome/browser/tabs/tab_model_list.h"
+#include "ios/chrome/browser/translate/chrome_ios_translate_client.h"
+#include "ios/chrome/browser/translate/translate_service_ios.h"
+#import "ios/chrome/browser/web_state_list/web_state_list.h"
+#include "ios/web/public/web_state/web_state.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
+
+IOSTranslateInternalsHandler::IOSTranslateInternalsHandler()
+    : scoped_web_state_list_observer_(
+          std::make_unique<ScopedObserver<WebStateList, WebStateListObserver>>(
+              this)),
+      scoped_tab_helper_observer_(
+          std::make_unique<ScopedObserver<
+              language::IOSLanguageDetectionTabHelper,
+              language::IOSLanguageDetectionTabHelper::Observer>>(this)) {}
+
+IOSTranslateInternalsHandler::~IOSTranslateInternalsHandler() {}
+
+translate::TranslateClient* IOSTranslateInternalsHandler::GetTranslateClient() {
+  return ChromeIOSTranslateClient::FromWebState(web_ui()->GetWebState());
+}
+
+variations::VariationsService*
+IOSTranslateInternalsHandler::GetVariationsService() {
+  return GetApplicationContext()->GetVariationsService();
+}
+
+void IOSTranslateInternalsHandler::RegisterMessageCallback(
+    const std::string& message,
+    const MessageCallback& callback) {
+  web_ui()->RegisterMessageCallback(message, callback);
+}
+
+void IOSTranslateInternalsHandler::CallJavascriptFunction(
+    const std::string& function_name,
+    const std::vector<const base::Value*>& args) {
+  web_ui()->CallJavascriptFunction(function_name, args);
+}
+
+void IOSTranslateInternalsHandler::RegisterMessages() {
+  web::BrowserState* browser_state = web_ui()->GetWebState()->GetBrowserState();
+  ios::ChromeBrowserState* chrome_browser_state =
+      ios::ChromeBrowserState::FromBrowserState(browser_state)
+          ->GetOriginalChromeBrowserState();
+  NSArray<TabModel*>* tab_models =
+      TabModelList::GetTabModelsForChromeBrowserState(chrome_browser_state);
+  for (TabModel* tab_model in tab_models) {
+    scoped_web_state_list_observer_->Add(tab_model.webStateList);
+    for (int i = 0; i < tab_model.webStateList->count(); i++) {
+      AddLanguageDetectionObserverForWebState(
+          tab_model.webStateList->GetWebStateAt(i));
+    }
+  }
+
+  RegisterMessageCallbacks();
+}
+
+void IOSTranslateInternalsHandler::WebStateInsertedAt(
+    WebStateList* web_state_list,
+    web::WebState* web_state,
+    int index,
+    bool activating) {
+  AddLanguageDetectionObserverForWebState(web_state);
+}
+
+void IOSTranslateInternalsHandler::WebStateReplacedAt(
+    WebStateList* web_state_list,
+    web::WebState* old_web_state,
+    web::WebState* new_web_state,
+    int index) {
+  RemoveLanguageDetectionObserverForWebState(old_web_state);
+  AddLanguageDetectionObserverForWebState(new_web_state);
+}
+
+void IOSTranslateInternalsHandler::WebStateDetachedAt(
+    WebStateList* web_state_list,
+    web::WebState* web_state,
+    int index) {
+  RemoveLanguageDetectionObserverForWebState(web_state);
+}
+
+void IOSTranslateInternalsHandler::OnLanguageDetermined(
+    const translate::LanguageDetectionDetails& details) {
+  if (web_ui()->GetWebState()->GetBrowserState()->IsOffTheRecord() ||
+      !GetTranslateClient()->IsTranslatableURL(details.url)) {
+    return;
+  }
+
+  AddLanguageDetectionDetails(details);
+}
+
+void IOSTranslateInternalsHandler::IOSLanguageDetectionTabHelperWasDestroyed(
+    language::IOSLanguageDetectionTabHelper* tab_helper) {
+  // No-op. The IOSLanguageDetectionTabHelper is stopped being observed in
+  // WebStateListObserver callbacks.
+}
+
+void IOSTranslateInternalsHandler::AddLanguageDetectionObserverForWebState(
+    web::WebState* web_state) {
+  language::IOSLanguageDetectionTabHelper* tab_helper =
+      language::IOSLanguageDetectionTabHelper::FromWebState(web_state);
+  if (!scoped_tab_helper_observer_->IsObserving(tab_helper)) {
+    scoped_tab_helper_observer_->Add(tab_helper);
+  }
+}
+
+void IOSTranslateInternalsHandler::RemoveLanguageDetectionObserverForWebState(
+    web::WebState* web_state) {
+  language::IOSLanguageDetectionTabHelper* tab_helper =
+      language::IOSLanguageDetectionTabHelper::FromWebState(web_state);
+  scoped_tab_helper_observer_->Remove(tab_helper);
+}
diff --git a/ios/chrome/browser/ui/webui/translate_internals/translate_internals_ui.h b/ios/chrome/browser/ui/webui/translate_internals/translate_internals_ui.h
new file mode 100644
index 0000000..dac50a5
--- /dev/null
+++ b/ios/chrome/browser/ui/webui/translate_internals/translate_internals_ui.h
@@ -0,0 +1,25 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef IOS_CHROME_BROWSER_UI_WEBUI_TRANSLATE_INTERNALS_TRANSLATE_INTERNALS_UI_H_
+#define IOS_CHROME_BROWSER_UI_WEBUI_TRANSLATE_INTERNALS_TRANSLATE_INTERNALS_UI_H_
+
+#include "base/macros.h"
+#include "ios/web/public/webui/web_ui_ios_controller.h"
+
+namespace web {
+class WebUIIOS;
+}
+
+// The WebUI controller for chrome://translate-internals.
+class TranslateInternalsUI : public web::WebUIIOSController {
+ public:
+  explicit TranslateInternalsUI(web::WebUIIOS* web_ui);
+  ~TranslateInternalsUI() override;
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(TranslateInternalsUI);
+};
+
+#endif  // IOS_CHROME_BROWSER_UI_WEBUI_TRANSLATE_INTERNALS_TRANSLATE_INTERNALS_UI_H_
diff --git a/ios/chrome/browser/ui/webui/translate_internals/translate_internals_ui.mm b/ios/chrome/browser/ui/webui/translate_internals/translate_internals_ui.mm
new file mode 100644
index 0000000..a3cf78cf
--- /dev/null
+++ b/ios/chrome/browser/ui/webui/translate_internals/translate_internals_ui.mm
@@ -0,0 +1,59 @@
+// 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 "ios/chrome/browser/ui/webui/translate_internals/translate_internals_ui.h"
+
+#include <string>
+
+#import "components/translate/translate_internals/translate_internals_handler.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/translate_internals/ios_translate_internals_handler.h"
+#include "ios/chrome/grit/ios_resources.h"
+#include "ios/web/public/web_ui_ios_data_source.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 {
+
+// Creates a WebUI data source for chrome://translate-internals page.
+// Changes to this should be in sync with its non-iOS equivalent
+// chrome/browser/ui/webui/translate_internals/translate_internals_ui.cc
+web::WebUIIOSDataSource* CreateTranslateInternalsHTMLSource() {
+  web::WebUIIOSDataSource* source =
+      web::WebUIIOSDataSource::Create(kChromeUITranslateInternalsHost);
+
+  source->SetDefaultResource(IDR_IOS_TRANSLATE_INTERNALS_HTML);
+  source->SetJsonPath("strings.js");
+  source->AddResourcePath("translate_internals.js",
+                          IDR_IOS_TRANSLATE_INTERNALS_JS);
+  source->UseGzip();
+
+  base::DictionaryValue langs;
+  translate::TranslateInternalsHandler::GetLanguages(&langs);
+  for (base::DictionaryValue::Iterator it(langs); !it.IsAtEnd(); it.Advance()) {
+    std::string key = "language-" + it.key();
+    std::string value;
+    it.value().GetAsString(&value);
+    source->AddString(key, value);
+  }
+
+  source->AddString("cld-version", "3");
+
+  return source;
+}
+
+}  // namespace
+
+TranslateInternalsUI::TranslateInternalsUI(web::WebUIIOS* web_ui)
+    : web::WebUIIOSController(web_ui) {
+  web_ui->AddMessageHandler(std::make_unique<IOSTranslateInternalsHandler>());
+  web::WebUIIOSDataSource::Add(ios::ChromeBrowserState::FromWebUIIOS(web_ui),
+                               CreateTranslateInternalsHTMLSource());
+}
+
+TranslateInternalsUI::~TranslateInternalsUI() {}
diff --git a/ios/web/shell/test/BUILD.gn b/ios/web/shell/test/BUILD.gn
index eb07f38..6cb8818 100644
--- a/ios/web/shell/test/BUILD.gn
+++ b/ios/web/shell/test/BUILD.gn
@@ -79,6 +79,8 @@
     "earl_grey/shell_actions.mm",
     "earl_grey/shell_earl_grey.h",
     "earl_grey/shell_earl_grey.mm",
+    "earl_grey/shell_earl_grey_app_interface.h",
+    "earl_grey/shell_earl_grey_app_interface.mm",
     "earl_grey/shell_matchers.h",
     "earl_grey/shell_matchers.mm",
     "earl_grey/shell_matchers_shorthand.h",
diff --git a/ios/web/shell/test/context_menu_egtest.mm b/ios/web/shell/test/context_menu_egtest.mm
index 091170452..8d050cd 100644
--- a/ios/web/shell/test/context_menu_egtest.mm
+++ b/ios/web/shell/test/context_menu_egtest.mm
@@ -54,10 +54,10 @@
   const char linkText[] = "normal-link-text";
   const GURL pageURL = _server.GetURL(kHtmlFile);
 
-  bool success = [ShellEarlGrey loadURL:pageURL];
+  bool success = shell_test_util::LoadUrl(pageURL);
   GREYAssert(success, @"Page did not complete loading.");
 
-  success = [ShellEarlGrey waitForWebViewContainingText:linkText];
+  success = shell_test_util::WaitForWebViewContainingText(linkText);
   GREYAssert(success, @"Failed waiting for web view containing '%s'", linkText);
 
   [[EarlGrey selectElementWithMatcher:web::WebView()]
@@ -85,9 +85,9 @@
   const char linkText[] = "no-webkit-link-text";
   const GURL pageURL = _server.GetURL(kHtmlFile);
 
-  bool success = [ShellEarlGrey loadURL:pageURL];
+  bool success = shell_test_util::LoadUrl(pageURL);
   GREYAssert(success, @"Page did not complete loading.");
-  success = [ShellEarlGrey waitForWebViewContainingText:linkText];
+  success = shell_test_util::WaitForWebViewContainingText(linkText);
   GREYAssert(success, @"Failed waiting for web view containing '%s'", linkText);
 
   [[EarlGrey selectElementWithMatcher:web::WebView()]
diff --git a/ios/web/shell/test/earl_grey/shell_earl_grey.h b/ios/web/shell/test/earl_grey/shell_earl_grey.h
index 66a0e88..b19ae4f 100644
--- a/ios/web/shell/test/earl_grey/shell_earl_grey.h
+++ b/ios/web/shell/test/earl_grey/shell_earl_grey.h
@@ -13,18 +13,18 @@
 // Test methods that perform actions on Web Shell. These methods may read or
 // alter Web Shell's internal state programmatically or via the UI, but in both
 // cases will properly synchronize the UI for Earl Grey tests.
-@interface ShellEarlGrey : NSObject
+namespace shell_test_util {
 
 // Loads |URL| in the current WebState with transition of type
 // ui::PAGE_TRANSITION_TYPED, and waits for the page to complete loading, or
 // a timeout. Returns false if the page doesn't finish loading, true if it
 // does.
-+ (bool)loadURL:(const GURL&)URL WARN_UNUSED_RESULT;
+bool LoadUrl(const GURL& url) WARN_UNUSED_RESULT;
 
 // Waits for the current web view to contain |text|. If the condition is not met
 // within a timeout, it returns false, otherwise, true.
-+ (bool)waitForWebViewContainingText:(const std::string)text WARN_UNUSED_RESULT;
+bool WaitForWebViewContainingText(const std::string& text) WARN_UNUSED_RESULT;
 
-@end
+}  // namespace shell_test_util
 
 #endif  // IOS_WEB_SHELL_TEST_EARL_GREY_SHELL_EARL_GREY_H_
diff --git a/ios/web/shell/test/earl_grey/shell_earl_grey.mm b/ios/web/shell/test/earl_grey/shell_earl_grey.mm
index 4878039..bf23f64 100644
--- a/ios/web/shell/test/earl_grey/shell_earl_grey.mm
+++ b/ios/web/shell/test/earl_grey/shell_earl_grey.mm
@@ -6,15 +6,11 @@
 
 #import <EarlGrey/EarlGrey.h>
 
+#import "base/strings/sys_string_conversions.h"
 #import "base/test/ios/wait_util.h"
-#import "ios/web/public/test/earl_grey/js_test_util.h"
-#include "ios/web/public/test/element_selector.h"
-#import "ios/web/public/test/navigation_test_util.h"
-#import "ios/web/public/test/web_view_content_test_util.h"
-#import "ios/web/public/test/web_view_interaction_test_util.h"
-#include "ios/web/shell/test/app/navigation_test_util.h"
-#import "ios/web/shell/test/app/web_shell_test_util.h"
+#import "ios/web/shell/test/earl_grey/shell_earl_grey_app_interface.h"
 
+using base::test::ios::kWaitForPageLoadTimeout;
 using base::test::ios::kWaitForUIElementTimeout;
 using base::test::ios::WaitUntilConditionOrTimeout;
 
@@ -22,19 +18,22 @@
 #error "This file requires ARC support."
 #endif
 
-@implementation ShellEarlGrey
+namespace shell_test_util {
 
-+ (bool)loadURL:(const GURL&)URL {
-  web::shell_test_util::LoadUrl(URL);
-  web::WebState* webState = web::shell_test_util::GetCurrentWebState();
+bool LoadUrl(const GURL& url) {
+  [ShellEarlGreyAppInterface loadURL:base::SysUTF8ToNSString(url.spec())];
 
-  if (!web::test::WaitForPageToFinishLoading(webState))
+  bool load_success = WaitUntilConditionOrTimeout(kWaitForPageLoadTimeout, ^{
+    return ![ShellEarlGreyAppInterface isCurrentWebStateLoading];
+  });
+  if (!load_success) {
     return false;
+  }
 
-  if (webState->ContentIsHTML()) {
-    if (!web::WaitUntilWindowIdInjected(webState)) {
-      return false;
-    }
+  bool injection_success =
+      [ShellEarlGreyAppInterface waitForWindowIDInjectedInCurrentWebState];
+  if (!injection_success) {
+    return false;
   }
 
   // Ensure any UI elements handled by EarlGrey become idle for any subsequent
@@ -43,11 +42,11 @@
   return true;
 }
 
-+ (bool)waitForWebViewContainingText:(std::string)text {
+bool WaitForWebViewContainingText(const std::string& text) {
   return WaitUntilConditionOrTimeout(kWaitForUIElementTimeout, ^bool {
-    return web::test::IsWebViewContainingText(
-        web::shell_test_util::GetCurrentWebState(), text);
+    return [ShellEarlGreyAppInterface
+        currentWebStateContainsText:base::SysUTF8ToNSString(text)];
   });
 }
 
-@end
+}  // namespace shell_test_util
diff --git a/ios/web/shell/test/earl_grey/shell_earl_grey_app_interface.h b/ios/web/shell/test/earl_grey/shell_earl_grey_app_interface.h
new file mode 100644
index 0000000..03eb265
--- /dev/null
+++ b/ios/web/shell/test/earl_grey/shell_earl_grey_app_interface.h
@@ -0,0 +1,34 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef IOS_WEB_SHELL_TEST_EARL_GREY_SHELL_EARL_GREY_APP_INTERFACE_H_
+#define IOS_WEB_SHELL_TEST_EARL_GREY_SHELL_EARL_GREY_APP_INTERFACE_H_
+
+#import <Foundation/Foundation.h>
+
+#include "base/compiler_specific.h"
+
+// Test methods that perform actions on Web Shell. These methods may read or
+// alter Web Shell's internal state programmatically or via the UI, but in both
+// cases will properly synchronize the UI for Earl Grey tests.
+@interface ShellEarlGreyAppInterface : NSObject
+
+// Loads |URL| in the current WebState with transition of type
+// ui::PAGE_TRANSITION_TYPED and returns without waiting for the page to load.
++ (void)loadURL:(NSString*)spec;
+
+// Returns YES if the current WebState is loading.
++ (BOOL)isCurrentWebStateLoading WARN_UNUSED_RESULT;
+
+// Returns YES if the windowID has been injected into the current web state.  If
+// the WebState contains content that does not require windowID injection,
+// returns YES immediately.
++ (BOOL)waitForWindowIDInjectedInCurrentWebState WARN_UNUSED_RESULT;
+
+// Returns YES if the current WebState contains the given |text|.
++ (BOOL)currentWebStateContainsText:(NSString*)text WARN_UNUSED_RESULT;
+
+@end
+
+#endif  // IOS_WEB_SHELL_TEST_EARL_GREY_SHELL_EARL_GREY_APP_INTERFACE_H_
diff --git a/ios/web/shell/test/earl_grey/shell_earl_grey_app_interface.mm b/ios/web/shell/test/earl_grey/shell_earl_grey_app_interface.mm
new file mode 100644
index 0000000..e751576
--- /dev/null
+++ b/ios/web/shell/test/earl_grey/shell_earl_grey_app_interface.mm
@@ -0,0 +1,42 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#import "ios/web/shell/test/earl_grey/shell_earl_grey_app_interface.h"
+
+#import <EarlGrey/EarlGrey.h>
+
+#import "base/strings/sys_string_conversions.h"
+#import "base/test/ios/wait_util.h"
+#import "ios/web/public/test/earl_grey/js_test_util.h"
+#import "ios/web/public/test/navigation_test_util.h"
+#import "ios/web/public/test/web_view_content_test_util.h"
+#import "ios/web/public/web_state/web_state.h"
+#import "ios/web/shell/test/app/web_shell_test_util.h"
+
+#if !defined(__has_feature) || !__has_feature(objc_arc)
+#error "This file requires ARC support."
+#endif
+
+using web::shell_test_util::GetCurrentWebState;
+
+@implementation ShellEarlGreyAppInterface
+
++ (void)loadURL:(NSString*)spec {
+  web::test::LoadUrl(GetCurrentWebState(), GURL(base::SysNSStringToUTF8(spec)));
+}
+
++ (BOOL)isCurrentWebStateLoading {
+  return GetCurrentWebState()->IsLoading();
+}
+
++ (BOOL)waitForWindowIDInjectedInCurrentWebState {
+  return web::WaitUntilWindowIdInjected(GetCurrentWebState());
+}
+
++ (BOOL)currentWebStateContainsText:(NSString*)text {
+  return web::test::IsWebViewContainingText(GetCurrentWebState(),
+                                            base::SysNSStringToUTF8(text));
+}
+
+@end
diff --git a/ios/web/shell/test/page_state_egtest.mm b/ios/web/shell/test/page_state_egtest.mm
index b7edc1a..f240bcdf 100644
--- a/ios/web/shell/test/page_state_egtest.mm
+++ b/ios/web/shell/test/page_state_egtest.mm
@@ -55,7 +55,7 @@
 // be {0, 0} before returning.
 void ScrollLongPageToTop(const GURL& url) {
   // Load the page and swipe down.
-  bool success = [ShellEarlGrey loadURL:url];
+  bool success = shell_test_util::LoadUrl(url);
   GREYAssert(success, @"Page did not complete loading.");
   [[EarlGrey selectElementWithMatcher:web::WebViewScrollView()]
       performAction:grey_scrollToContentEdge(kGREYContentEdgeTop)];
@@ -113,7 +113,7 @@
 - (void)testZeroContentOffsetAfterLoad {
   // Set up the file-based server to load the tall page.
   const GURL baseURL = _server.GetURL(kLongPage1);
-  bool success = [ShellEarlGrey loadURL:baseURL];
+  bool success = shell_test_util::LoadUrl(baseURL);
   GREYAssert(success, @"Page did not complete loading.");
 
   // Scroll the page and load again to verify that the new page's scroll offset
@@ -127,8 +127,9 @@
     // Add a query parameter so the next load creates another NavigationItem.
     GURL::Replacements replacements;
     replacements.SetQueryStr(base::NumberToString(i));
-    GREYAssert([ShellEarlGrey loadURL:baseURL.ReplaceComponents(replacements)],
-               @"Page did not complete loading.");
+    bool success =
+        shell_test_util::LoadUrl(baseURL.ReplaceComponents(replacements));
+    GREYAssert(success, @"Page did not complete loading.");
     // Wait for the content offset to be set to {0, 0}.
     WaitForOffset(0.0);
   }
diff --git a/ios/web/web_state/js/resources/message.js b/ios/web/web_state/js/resources/message.js
index 3a75562..b723725 100644
--- a/ios/web/web_state/js/resources/message.js
+++ b/ios/web/web_state/js/resources/message.js
@@ -273,8 +273,7 @@
   getFrameSymmetricKey_(function(frameKey) {
     window.crypto.subtle.decrypt(algorithm, frameKey, encryptedFunctionArray)
         .then(function(decrypted) {
-      var callJSON =
-          String.fromCharCode.apply(null, new Uint8Array(decrypted));
+      var callJSON = new TextDecoder().decode(new Uint8Array(decrypted));
       var callDict = JSON.parse(callJSON);
 
       // Verify that message id is valid.
diff --git a/ios/web/web_state/web_state_observer_inttest.mm b/ios/web/web_state/web_state_observer_inttest.mm
index 3433ba1..912ae0edb 100644
--- a/ios/web/web_state/web_state_observer_inttest.mm
+++ b/ios/web/web_state/web_state_observer_inttest.mm
@@ -2034,13 +2034,7 @@
 }
 
 // Tests failed load after the navigation is sucessfully finished.
-// TODO(crbug.com/845879): test is flaky (probably since crrev.com/1056203).
-#if TARGET_IPHONE_SIMULATOR
-#define MAYBE_FailedLoad FailedLoad
-#else
-#define MAYBE_FailedLoad FLAKY_FailedLoad
-#endif
-TEST_P(WebStateObserverTest, FLAKY_FailedLoad) {
+TEST_P(WebStateObserverTest, FailedLoad) {
   GURL url = test_server_->GetURL("/exabyte_response");
 
   NavigationContext* context = nullptr;
diff --git a/media/capture/video/OWNERS b/media/capture/video/OWNERS
index ea5ac3b3..8b3d667 100644
--- a/media/capture/video/OWNERS
+++ b/media/capture/video/OWNERS
@@ -1,7 +1,9 @@
 emircan@chromium.org
 chfremer@chromium.org
-mcasas@chromium.org
 tommi@chromium.org
 
-# TEAM: media-capture-and-streams@grotations.appspotmail.com
+# Original (legacy) owner.
+mcasas@chromium.org
+
 # COMPONENT: Blink>GetUserMedia>WebCam
+# TEAM: webrtc-dev@chromium.org
diff --git a/media/capture/video/android/java/src/org/chromium/media/OWNERS b/media/capture/video/android/java/src/org/chromium/media/OWNERS
index e34b150738..939f3d261 100644
--- a/media/capture/video/android/java/src/org/chromium/media/OWNERS
+++ b/media/capture/video/android/java/src/org/chromium/media/OWNERS
@@ -1,5 +1,7 @@
-mcasas@chromium.org
 qinmin@chromium.org
 
-# media-capture-and-streams@grotations.appspotmail.com
+# Original (legacy) owner.
+mcasas@chromium.org
+
 # COMPONENT: Blink>GetUserMedia>WebCam
+# TEAM: webrtc-dev@chromium.org
diff --git a/media/muxers/OWNERS b/media/muxers/OWNERS
index 2198557..92570f5 100644
--- a/media/muxers/OWNERS
+++ b/media/muxers/OWNERS
@@ -1,4 +1,7 @@
-mcasas@chromium.org
 miu@chromium.org
 
+# Original (legacy) owner.
+mcasas@chromium.org
+
 # COMPONENT: Blink>MediaRecording
+# TEAM: webrtc-dev@chromium.org
diff --git a/net/BUILD.gn b/net/BUILD.gn
index f2b371d..efc93fa8 100644
--- a/net/BUILD.gn
+++ b/net/BUILD.gn
@@ -2329,6 +2329,14 @@
   }
 }
 
+if (is_android) {
+  java_cpp_enum("effective_connection_type_java") {
+    sources = [
+      "//net/nqe/effective_connection_type.h",
+    ]
+  }
+}
+
 grit("net_resources") {
   source = "base/net_resources.grd"
   outputs = [
diff --git a/net/disk_cache/blockfile/entry_impl.cc b/net/disk_cache/blockfile/entry_impl.cc
index 9bc513c..7323333 100644
--- a/net/disk_cache/blockfile/entry_impl.cc
+++ b/net/disk_cache/blockfile/entry_impl.cc
@@ -183,6 +183,12 @@
   DCHECK_GE(offset, 0);
   DCHECK_GE(len, 0);
   DCHECK_GE(offset + len, 0);
+
+  // 0-length writes that don't extend can just be ignored here, and are safe
+  // even if they're are before offset_, as truncates are handled elsewhere.
+  if (len == 0 && offset < End())
+    return;
+
   DCHECK_GE(offset, offset_);
   DVLOG(3) << "Buffer write at " << offset << " current " << offset_;
 
diff --git a/net/disk_cache/entry_unittest.cc b/net/disk_cache/entry_unittest.cc
index 2fe639b8..0611436a 100644
--- a/net/disk_cache/entry_unittest.cc
+++ b/net/disk_cache/entry_unittest.cc
@@ -94,6 +94,7 @@
   void CloseSparseAfterBackendDestruction();
   void LastUsedTimePersists();
   void TruncateBackwards();
+  void ZeroWriteBackwards();
 };
 
 // This part of the test runs on the background thread.
@@ -5155,6 +5156,56 @@
   TruncateBackwards();
 }
 
+void DiskCacheEntryTest::ZeroWriteBackwards() {
+  const char kKey[] = "a key";
+
+  disk_cache::Entry* entry = nullptr;
+  ASSERT_THAT(CreateEntry(kKey, &entry), IsOk());
+  ASSERT_TRUE(entry != nullptr);
+
+  const int kSize = 1024;
+  scoped_refptr<net::IOBuffer> buffer =
+      base::MakeRefCounted<net::IOBuffer>(kSize);
+  CacheTestFillBuffer(buffer->data(), kSize, false);
+
+  // Offset here needs to be > blockfile's kMaxBlockSize to hit
+  // https://crbug.com/946538, as writes close to beginning are handled
+  // specially.
+  EXPECT_EQ(0, WriteData(entry, /* stream_index = */ 0,
+                         /* offset = */ 17000, buffer.get(),
+                         /* size = */ 0, /* truncate = */ true));
+
+  EXPECT_EQ(0, WriteData(entry, /* stream_index = */ 0,
+                         /* offset = */ 0, buffer.get(),
+                         /* size = */ 0, /* truncate = */ false));
+
+  EXPECT_EQ(kSize, ReadData(entry, /* stream_index = */ 0,
+                            /* offset = */ 0, buffer.get(),
+                            /* size = */ kSize));
+  for (int i = 0; i < kSize; ++i) {
+    EXPECT_EQ(0, buffer->data()[i]) << i;
+  }
+  entry->Close();
+}
+
+TEST_F(DiskCacheEntryTest, ZeroWriteBackwards) {
+  // https://crbug.com/946538/
+  InitCache();
+  ZeroWriteBackwards();
+}
+
+TEST_F(DiskCacheEntryTest, SimpleZeroWriteBackwards) {
+  SetSimpleCacheMode();
+  InitCache();
+  ZeroWriteBackwards();
+}
+
+TEST_F(DiskCacheEntryTest, MemoryOnlyZeroWriteBackwards) {
+  SetMemoryOnlyMode();
+  InitCache();
+  ZeroWriteBackwards();
+}
+
 TEST_F(DiskCacheEntryTest, SimpleCacheCloseResurrection) {
   const int kSize = 10;
   scoped_refptr<net::IOBuffer> buffer =
diff --git a/net/dns/BUILD.gn b/net/dns/BUILD.gn
index f4da496..d6fb54bd 100644
--- a/net/dns/BUILD.gn
+++ b/net/dns/BUILD.gn
@@ -213,6 +213,10 @@
   # Whitelist-only access so we can keep track of all usage external to the
   # network stack and network service.
   friend = [
+    # chromecast/browser/url_request_context_factory.cc
+    # URLRequestContext creation for chromecast.
+    "//chromecast/browser",
+
     # Network stack/service.
     "//net/*",
     "//services/network/*",
@@ -352,6 +356,7 @@
 source_set("tests") {
   testonly = true
   sources = [
+    "context_host_resolver_unittest.cc",
     "dns_config_service_unittest.cc",
     "dns_config_service_win_unittest.cc",
     "dns_hosts_unittest.cc",
diff --git a/net/dns/context_host_resolver.cc b/net/dns/context_host_resolver.cc
index 5fd0b3a..31a6d7e 100644
--- a/net/dns/context_host_resolver.cc
+++ b/net/dns/context_host_resolver.cc
@@ -4,6 +4,7 @@
 
 #include "net/dns/context_host_resolver.h"
 
+#include <string>
 #include <utility>
 
 #include "base/logging.h"
@@ -17,6 +18,67 @@
 
 namespace net {
 
+// Wrapper of ResolveHostRequests that on destruction will remove itself from
+// |ContextHostResolver::active_requests_|.
+class ContextHostResolver::WrappedRequest
+    : public HostResolver::ResolveHostRequest {
+ public:
+  WrappedRequest(
+      std::unique_ptr<HostResolverManager::CancellableRequest> inner_request,
+      ContextHostResolver* resolver)
+      : inner_request_(std::move(inner_request)), resolver_(resolver) {}
+
+  ~WrappedRequest() override { Cancel(); }
+
+  void Cancel() {
+    // Cannot destroy |inner_request_| because it is still allowed to call
+    // Get...Results() methods if the request was already complete.
+    inner_request_->Cancel();
+
+    if (resolver_) {
+      resolver_->active_requests_.erase(this);
+      resolver_ = nullptr;
+    }
+  }
+
+  int Start(CompletionOnceCallback callback) override {
+    DCHECK(resolver_);
+    return inner_request_->Start(std::move(callback));
+  }
+
+  const base::Optional<AddressList>& GetAddressResults() const override {
+    return inner_request_->GetAddressResults();
+  }
+
+  const base::Optional<std::vector<std::string>>& GetTextResults()
+      const override {
+    return inner_request_->GetTextResults();
+  }
+
+  const base::Optional<std::vector<HostPortPair>>& GetHostnameResults()
+      const override {
+    return inner_request_->GetHostnameResults();
+  }
+
+  const base::Optional<HostCache::EntryStaleness>& GetStaleInfo()
+      const override {
+    return inner_request_->GetStaleInfo();
+  }
+
+  void ChangeRequestPriority(RequestPriority priority) override {
+    inner_request_->ChangeRequestPriority(priority);
+  }
+
+ private:
+  std::unique_ptr<HostResolverManager::CancellableRequest> inner_request_;
+
+  // Resolver is expected to call Cancel() on destruction, clearing the pointer
+  // before it becomes invalid.
+  ContextHostResolver* resolver_;
+
+  DISALLOW_COPY_AND_ASSIGN(WrappedRequest);
+};
+
 ContextHostResolver::ContextHostResolver(HostResolverManager* manager)
     : manager_(manager) {
   DCHECK(manager_);
@@ -31,6 +93,10 @@
 ContextHostResolver::~ContextHostResolver() {
   if (owned_manager_)
     DCHECK_EQ(owned_manager_.get(), manager_);
+
+  // Silently cancel all requests associated with this resolver.
+  while (!active_requests_.empty())
+    (*active_requests_.begin())->Cancel();
 }
 
 std::unique_ptr<HostResolver::ResolveHostRequest>
@@ -39,7 +105,10 @@
     const NetLogWithSource& source_net_log,
     const base::Optional<ResolveHostParameters>& optional_parameters) {
   // TODO(crbug.com/934402): DHCECK |context_| once universally set.
-  return manager_->CreateRequest(host, source_net_log, optional_parameters);
+  auto request = std::make_unique<WrappedRequest>(
+      manager_->CreateRequest(host, source_net_log, optional_parameters), this);
+  active_requests_.insert(request.get());
+  return request;
 }
 
 std::unique_ptr<HostResolver::MdnsListener>
diff --git a/net/dns/context_host_resolver.h b/net/dns/context_host_resolver.h
index 76382ee..0f8f5be9 100644
--- a/net/dns/context_host_resolver.h
+++ b/net/dns/context_host_resolver.h
@@ -6,6 +6,7 @@
 #define NET_DNS_CONTEXT_HOST_RESOLVER_H_
 
 #include <memory>
+#include <unordered_set>
 #include <vector>
 
 #include "base/macros.h"
@@ -75,10 +76,20 @@
   void SetBaseDnsConfigForTesting(const DnsConfig& base_config);
   void SetTickClockForTesting(const base::TickClock* tick_clock);
 
+  size_t GetNumActiveRequestsForTesting() const {
+    return active_requests_.size();
+  }
+
  private:
+  class WrappedRequest;
+
   HostResolverManager* const manager_;
   std::unique_ptr<HostResolverManager> owned_manager_;
 
+  // Requests are expected to clear themselves from this set on destruction or
+  // cancellation.
+  std::unordered_set<WrappedRequest*> active_requests_;
+
   URLRequestContext* context_ = nullptr;
 
   DISALLOW_COPY_AND_ASSIGN(ContextHostResolver);
diff --git a/net/dns/context_host_resolver_unittest.cc b/net/dns/context_host_resolver_unittest.cc
new file mode 100644
index 0000000..7ff08ad
--- /dev/null
+++ b/net/dns/context_host_resolver_unittest.cc
@@ -0,0 +1,251 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/dns/context_host_resolver.h"
+
+#include <utility>
+
+#include "base/bind.h"
+#include "base/optional.h"
+#include "base/run_loop.h"
+#include "net/base/host_port_pair.h"
+#include "net/base/ip_address.h"
+#include "net/base/ip_endpoint.h"
+#include "net/base/net_errors.h"
+#include "net/base/network_change_notifier.h"
+#include "net/base/test_completion_callback.h"
+#include "net/dns/dns_config.h"
+#include "net/dns/dns_test_util.h"
+#include "net/dns/dns_util.h"
+#include "net/dns/host_resolver_manager.h"
+#include "net/dns/mock_host_resolver.h"
+#include "net/dns/public/dns_protocol.h"
+#include "net/log/net_log_with_source.h"
+#include "net/test/gtest_util.h"
+#include "net/test/test_with_scoped_task_environment.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace net {
+
+namespace {
+const IPEndPoint kEndpoint(IPAddress(1, 2, 3, 4), 100);
+}
+
+class ContextHostResolverTest : public TestWithScopedTaskEnvironment {
+ protected:
+  void SetUp() override {
+    manager_ =
+        std::make_unique<HostResolverManager>(HostResolver::Options(), nullptr);
+  }
+
+  void SetMockDnsRules(MockDnsClientRuleList rules) {
+    // HostResolver expects DnsConfig to get set after setting DnsClient, so
+    // create first with an empty config and then update the config.
+    auto dns_client =
+        std::make_unique<MockDnsClient>(DnsConfig(), std::move(rules));
+    dns_client_ = dns_client.get();
+    manager_->SetDnsClient(std::move(dns_client));
+
+    scoped_refptr<HostResolverProc> proc = CreateCatchAllHostResolverProc();
+    manager_->set_proc_params_for_test(ProcTaskParams(proc.get(), 1u));
+
+    IPAddress dns_ip(192, 168, 1, 0);
+    DnsConfig config;
+    config.nameservers.push_back(
+        IPEndPoint(dns_ip, dns_protocol::kDefaultPort));
+    EXPECT_TRUE(config.IsValid());
+    manager_->SetBaseDnsConfigForTesting(config);
+  }
+
+  MockDnsClient* dns_client_;
+  std::unique_ptr<HostResolverManager> manager_;
+};
+
+TEST_F(ContextHostResolverTest, Resolve) {
+  MockDnsClientRuleList rules;
+  rules.emplace_back("example.com", dns_protocol::kTypeA,
+                     SecureDnsMode::AUTOMATIC,
+                     MockDnsClientRule::Result(BuildTestDnsResponse(
+                         "example.com", kEndpoint.address())),
+                     false /* delay */);
+  rules.emplace_back(
+      "example.com", dns_protocol::kTypeAAAA, SecureDnsMode::AUTOMATIC,
+      MockDnsClientRule::Result(MockDnsClientRule::EMPTY), false /* delay */);
+  SetMockDnsRules(std::move(rules));
+
+  auto resolver = std::make_unique<ContextHostResolver>(manager_.get());
+  std::unique_ptr<HostResolver::ResolveHostRequest> request =
+      resolver->CreateRequest(HostPortPair("example.com", 100),
+                              NetLogWithSource(), base::nullopt);
+
+  TestCompletionCallback callback;
+  int rv = request->Start(callback.callback());
+  EXPECT_THAT(callback.GetResult(rv), test::IsOk());
+  EXPECT_THAT(request->GetAddressResults().value().endpoints(),
+              testing::ElementsAre(kEndpoint));
+}
+
+// Test that destroying a request silently cancels that request.
+TEST_F(ContextHostResolverTest, DestroyRequest) {
+  // Setup delayed results for "example.com".
+  MockDnsClientRuleList rules;
+  rules.emplace_back("example.com", dns_protocol::kTypeA,
+                     SecureDnsMode::AUTOMATIC,
+                     MockDnsClientRule::Result(BuildTestDnsResponse(
+                         "example.com", IPAddress(1, 2, 3, 4))),
+                     true /* delay */);
+  rules.emplace_back(
+      "example.com", dns_protocol::kTypeAAAA, SecureDnsMode::AUTOMATIC,
+      MockDnsClientRule::Result(MockDnsClientRule::EMPTY), false /* delay */);
+  SetMockDnsRules(std::move(rules));
+
+  auto resolver = std::make_unique<ContextHostResolver>(manager_.get());
+  std::unique_ptr<HostResolver::ResolveHostRequest> request =
+      resolver->CreateRequest(HostPortPair("example.com", 100),
+                              NetLogWithSource(), base::nullopt);
+  EXPECT_EQ(1u, resolver->GetNumActiveRequestsForTesting());
+
+  TestCompletionCallback callback;
+  int rv = request->Start(callback.callback());
+
+  // Cancel |request| before allowing delayed result to complete.
+  request = nullptr;
+  dns_client_->CompleteDelayedTransactions();
+
+  // Ensure |request| never completes.
+  base::RunLoop().RunUntilIdle();
+  EXPECT_THAT(rv, test::IsError(ERR_IO_PENDING));
+  EXPECT_FALSE(callback.have_result());
+  EXPECT_EQ(0u, resolver->GetNumActiveRequestsForTesting());
+}
+
+// Test that cancelling a resolver cancels its (and only its) requests.
+TEST_F(ContextHostResolverTest, DestroyResolver) {
+  // Setup delayed results for "example.com" and "google.com".
+  MockDnsClientRuleList rules;
+  rules.emplace_back("example.com", dns_protocol::kTypeA,
+                     SecureDnsMode::AUTOMATIC,
+                     MockDnsClientRule::Result(BuildTestDnsResponse(
+                         "example.com", IPAddress(2, 3, 4, 5))),
+                     true /* delay */);
+  rules.emplace_back(
+      "example.com", dns_protocol::kTypeAAAA, SecureDnsMode::AUTOMATIC,
+      MockDnsClientRule::Result(MockDnsClientRule::EMPTY), false /* delay */);
+  rules.emplace_back("google.com", dns_protocol::kTypeA,
+                     SecureDnsMode::AUTOMATIC,
+                     MockDnsClientRule::Result(BuildTestDnsResponse(
+                         "google.com", kEndpoint.address())),
+                     true /* delay */);
+  rules.emplace_back(
+      "google.com", dns_protocol::kTypeAAAA, SecureDnsMode::AUTOMATIC,
+      MockDnsClientRule::Result(MockDnsClientRule::EMPTY), false /* delay */);
+  SetMockDnsRules(std::move(rules));
+
+  auto resolver1 = std::make_unique<ContextHostResolver>(manager_.get());
+  std::unique_ptr<HostResolver::ResolveHostRequest> request1 =
+      resolver1->CreateRequest(HostPortPair("example.com", 100),
+                               NetLogWithSource(), base::nullopt);
+  auto resolver2 = std::make_unique<ContextHostResolver>(manager_.get());
+  std::unique_ptr<HostResolver::ResolveHostRequest> request2 =
+      resolver2->CreateRequest(HostPortPair("google.com", 100),
+                               NetLogWithSource(), base::nullopt);
+
+  TestCompletionCallback callback1;
+  int rv1 = request1->Start(callback1.callback());
+  TestCompletionCallback callback2;
+  int rv2 = request2->Start(callback2.callback());
+
+  EXPECT_EQ(2u, manager_->num_jobs_for_testing());
+
+  // Cancel |resolver1| before allowing delayed requests to complete.
+  resolver1 = nullptr;
+  dns_client_->CompleteDelayedTransactions();
+
+  EXPECT_THAT(callback2.GetResult(rv2), test::IsOk());
+  EXPECT_THAT(request2->GetAddressResults().value().endpoints(),
+              testing::ElementsAre(kEndpoint));
+
+  // Ensure |request1| never completes.
+  base::RunLoop().RunUntilIdle();
+  EXPECT_THAT(rv1, test::IsError(ERR_IO_PENDING));
+  EXPECT_FALSE(callback1.have_result());
+}
+
+// Test that cancelling a resolver cancels its (and only its) requests, even if
+// those requests shared a job (same query) with another resolver's requests.
+TEST_F(ContextHostResolverTest, DestroyResolver_RemainingRequests) {
+  // Setup delayed results for "example.com".
+  MockDnsClientRuleList rules;
+  rules.emplace_back("example.com", dns_protocol::kTypeA,
+                     SecureDnsMode::AUTOMATIC,
+                     MockDnsClientRule::Result(BuildTestDnsResponse(
+                         "example.com", kEndpoint.address())),
+                     true /* delay */);
+  rules.emplace_back(
+      "example.com", dns_protocol::kTypeAAAA, SecureDnsMode::AUTOMATIC,
+      MockDnsClientRule::Result(MockDnsClientRule::EMPTY), false /* delay */);
+  SetMockDnsRules(std::move(rules));
+
+  // Make ResolveHostRequests the same hostname for both resolvers.
+  auto resolver1 = std::make_unique<ContextHostResolver>(manager_.get());
+  std::unique_ptr<HostResolver::ResolveHostRequest> request1 =
+      resolver1->CreateRequest(HostPortPair("example.com", 100),
+                               NetLogWithSource(), base::nullopt);
+  auto resolver2 = std::make_unique<ContextHostResolver>(manager_.get());
+  std::unique_ptr<HostResolver::ResolveHostRequest> request2 =
+      resolver2->CreateRequest(HostPortPair("example.com", 100),
+                               NetLogWithSource(), base::nullopt);
+
+  TestCompletionCallback callback1;
+  int rv1 = request1->Start(callback1.callback());
+  TestCompletionCallback callback2;
+  int rv2 = request2->Start(callback2.callback());
+
+  // Test relies on assumption that requests share jobs, so assert just 1.
+  ASSERT_EQ(1u, manager_->num_jobs_for_testing());
+
+  // Cancel |resolver1| before allowing delayed requests to complete.
+  resolver1 = nullptr;
+  dns_client_->CompleteDelayedTransactions();
+
+  EXPECT_THAT(callback2.GetResult(rv2), test::IsOk());
+  EXPECT_THAT(request2->GetAddressResults().value().endpoints(),
+              testing::ElementsAre(kEndpoint));
+
+  // Ensure |request1| never completes.
+  base::RunLoop().RunUntilIdle();
+  EXPECT_THAT(rv1, test::IsError(ERR_IO_PENDING));
+  EXPECT_FALSE(callback1.have_result());
+}
+
+TEST_F(ContextHostResolverTest, DestroyResolver_CompletedRequests) {
+  MockDnsClientRuleList rules;
+  rules.emplace_back("example.com", dns_protocol::kTypeA,
+                     SecureDnsMode::AUTOMATIC,
+                     MockDnsClientRule::Result(BuildTestDnsResponse(
+                         "example.com", kEndpoint.address())),
+                     false /* delay */);
+  rules.emplace_back(
+      "example.com", dns_protocol::kTypeAAAA, SecureDnsMode::AUTOMATIC,
+      MockDnsClientRule::Result(MockDnsClientRule::EMPTY), false /* delay */);
+  SetMockDnsRules(std::move(rules));
+
+  auto resolver = std::make_unique<ContextHostResolver>(manager_.get());
+  std::unique_ptr<HostResolver::ResolveHostRequest> request =
+      resolver->CreateRequest(HostPortPair("example.com", 100),
+                              NetLogWithSource(), base::nullopt);
+
+  // Complete request and then destroy the resolver.
+  TestCompletionCallback callback;
+  int rv = request->Start(callback.callback());
+  ASSERT_THAT(callback.GetResult(rv), test::IsOk());
+  resolver = nullptr;
+
+  // Expect completed results are still available.
+  EXPECT_THAT(request->GetAddressResults().value().endpoints(),
+              testing::ElementsAre(kEndpoint));
+}
+
+}  // namespace net
diff --git a/net/dns/host_resolver_manager.cc b/net/dns/host_resolver_manager.cc
index cc6365a..e740cffb 100644
--- a/net/dns/host_resolver_manager.cc
+++ b/net/dns/host_resolver_manager.cc
@@ -490,7 +490,7 @@
 // cancellation is initiated by the Job (OnJobCancelled) vs by the end user
 // (~RequestImpl).
 class HostResolverManager::RequestImpl
-    : public HostResolver::ResolveHostRequest,
+    : public CancellableRequest,
       public base::LinkNode<HostResolverManager::RequestImpl> {
  public:
   RequestImpl(const NetLogWithSource& source_net_log,
@@ -508,7 +508,12 @@
         resolver_(resolver),
         complete_(false) {}
 
-  ~RequestImpl() override;
+  ~RequestImpl() override {
+    DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+    Cancel();
+  }
+
+  void Cancel() override;
 
   int Start(CompletionOnceCallback callback) override {
     DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
@@ -2252,7 +2257,7 @@
   UpdateModeForHistogram(dns_config);
 }
 
-std::unique_ptr<HostResolver::ResolveHostRequest>
+std::unique_ptr<HostResolverManager::CancellableRequest>
 HostResolverManager::CreateRequest(
     const HostPortPair& host,
     const NetLogWithSource& net_log,
@@ -3133,10 +3138,14 @@
   }
 }
 
-HostResolverManager::RequestImpl::~RequestImpl() {
+void HostResolverManager::RequestImpl::Cancel() {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-  if (job_)
-    job_->CancelRequest(this);
+  if (!job_)
+    return;
+
+  job_->CancelRequest(this);
+  job_ = nullptr;
+  callback_.Reset();
 }
 
 void HostResolverManager::RequestImpl::ChangeRequestPriority(
diff --git a/net/dns/host_resolver_manager.h b/net/dns/host_resolver_manager.h
index 0dadfafd..e87c15f5 100644
--- a/net/dns/host_resolver_manager.h
+++ b/net/dns/host_resolver_manager.h
@@ -87,6 +87,14 @@
   using ResolveHostRequest = HostResolver::ResolveHostRequest;
   using ResolveHostParameters = HostResolver::ResolveHostParameters;
 
+  class CancellableRequest : public ResolveHostRequest {
+   public:
+    // If running asynchronously, silently cancels the request as if destroyed.
+    // Callbacks will never be invoked. Noop if request is already complete or
+    // never started.
+    virtual void Cancel() = 0;
+  };
+
   // Creates a HostResolver as specified by |options|. Blocking tasks are run in
   // TaskScheduler.
   //
@@ -111,8 +119,7 @@
   // NetworkChangeNotifier.
   void SetDnsClient(std::unique_ptr<DnsClient> dns_client);
 
-  // HostResolver methods:
-  std::unique_ptr<ResolveHostRequest> CreateRequest(
+  std::unique_ptr<CancellableRequest> CreateRequest(
       const HostPortPair& host,
       const NetLogWithSource& net_log,
       const base::Optional<ResolveHostParameters>& optional_parameters);
@@ -159,6 +166,15 @@
 
   void SetBaseDnsConfigForTesting(const DnsConfig& base_config);
 
+  // Allows the tests to catch slots leaking out of the dispatcher.  One
+  // HostResolverManager::Job could occupy multiple PrioritizedDispatcher job
+  // slots.
+  size_t num_running_dispatcher_jobs_for_tests() const {
+    return dispatcher_->num_running_jobs();
+  }
+
+  size_t num_jobs_for_testing() const { return jobs_.size(); }
+
  protected:
   // Callback from HaveOnlyLoopbackAddresses probe.
   void SetHaveOnlyLoopbackAddresses(bool result);
@@ -332,13 +348,6 @@
 
   int GetOrCreateMdnsClient(MDnsClient** out_client);
 
-  // Allows the tests to catch slots leaking out of the dispatcher.  One
-  // HostResolverManager::Job could occupy multiple PrioritizedDispatcher job
-  // slots.
-  size_t num_running_dispatcher_jobs_for_tests() const {
-    return dispatcher_->num_running_jobs();
-  }
-
   // Update |mode_for_histogram_|. Called when DNS config changes. |dns_config|
   // is the current DNS config and is only used if !HaveDnsConfig().
   void UpdateModeForHistogram(const DnsConfig& dns_config);
diff --git a/net/dns/host_resolver_manager_unittest.cc b/net/dns/host_resolver_manager_unittest.cc
index 2a82cf3a..bde8668 100644
--- a/net/dns/host_resolver_manager_unittest.cc
+++ b/net/dns/host_resolver_manager_unittest.cc
@@ -244,13 +244,13 @@
 
   ResolveHostResponseHelper() {}
   explicit ResolveHostResponseHelper(
-      std::unique_ptr<HostResolver::ResolveHostRequest> request)
+      std::unique_ptr<HostResolverManager::CancellableRequest> request)
       : request_(std::move(request)) {
     result_error_ = request_->Start(base::BindOnce(
         &ResolveHostResponseHelper::OnComplete, base::Unretained(this)));
   }
   ResolveHostResponseHelper(
-      std::unique_ptr<HostResolver::ResolveHostRequest> request,
+      std::unique_ptr<HostResolverManager::CancellableRequest> request,
       Callback custom_callback)
       : request_(std::move(request)) {
     result_error_ = request_->Start(
@@ -265,7 +265,7 @@
     return result_error_;
   }
 
-  HostResolver::ResolveHostRequest* request() { return request_.get(); }
+  HostResolverManager::CancellableRequest* request() { return request_.get(); }
 
   void CancelRequest() {
     DCHECK(request_);
@@ -291,7 +291,7 @@
     DCHECK(complete());
   }
 
-  std::unique_ptr<HostResolver::ResolveHostRequest> request_;
+  std::unique_ptr<HostResolverManager::CancellableRequest> request_;
   int result_error_ = ERR_IO_PENDING;
   base::RunLoop run_loop_;
 
@@ -3993,6 +3993,57 @@
   }
 }
 
+TEST_F(HostResolverManagerDnsTest, DeleteWithCompletedRequests) {
+  ChangeDnsConfig(CreateValidDnsConfig());
+
+  ResolveHostResponseHelper response(resolver_->CreateRequest(
+      HostPortPair("ok", 80), NetLogWithSource(), base::nullopt));
+
+  EXPECT_THAT(response.result_error(), IsOk());
+  EXPECT_THAT(response.request()->GetAddressResults().value().endpoints(),
+              testing::UnorderedElementsAre(CreateExpected("127.0.0.1", 80),
+                                            CreateExpected("::1", 80)));
+
+  resolver_.reset();
+
+  // Completed requests should be unaffected by manager destruction.
+  EXPECT_THAT(response.request()->GetAddressResults().value().endpoints(),
+              testing::UnorderedElementsAre(CreateExpected("127.0.0.1", 80),
+                                            CreateExpected("::1", 80)));
+}
+
+TEST_F(HostResolverManagerDnsTest, ExplicitCancel) {
+  ChangeDnsConfig(CreateValidDnsConfig());
+
+  ResolveHostResponseHelper response(resolver_->CreateRequest(
+      HostPortPair("4slow_4ok", 80), NetLogWithSource(), base::nullopt));
+
+  response.request()->Cancel();
+  dns_client_->CompleteDelayedTransactions();
+
+  base::RunLoop().RunUntilIdle();
+  EXPECT_FALSE(response.complete());
+}
+
+TEST_F(HostResolverManagerDnsTest, ExplicitCancel_Completed) {
+  ChangeDnsConfig(CreateValidDnsConfig());
+
+  ResolveHostResponseHelper response(resolver_->CreateRequest(
+      HostPortPair("ok", 80), NetLogWithSource(), base::nullopt));
+
+  EXPECT_THAT(response.result_error(), IsOk());
+  EXPECT_THAT(response.request()->GetAddressResults().value().endpoints(),
+              testing::UnorderedElementsAre(CreateExpected("127.0.0.1", 80),
+                                            CreateExpected("::1", 80)));
+
+  response.request()->Cancel();
+
+  // Completed requests should be unaffected by cancellation.
+  EXPECT_THAT(response.request()->GetAddressResults().value().endpoints(),
+              testing::UnorderedElementsAre(CreateExpected("127.0.0.1", 80),
+                                            CreateExpected("::1", 80)));
+}
+
 // Cancel a request with only the IPv6 transaction active.
 TEST_F(HostResolverManagerDnsTest, CancelWithIPv6TransactionActive) {
   ChangeDnsConfig(CreateValidDnsConfig());
diff --git a/net/http/http_basic_state.cc b/net/http/http_basic_state.cc
index 3c1eb58..a9efbe1 100644
--- a/net/http/http_basic_state.cc
+++ b/net/http/http_basic_state.cc
@@ -24,7 +24,6 @@
     : read_buf_(base::MakeRefCounted<GrowableIOBuffer>()),
       connection_(std::move(connection)),
       using_proxy_(using_proxy),
-      can_send_early_(false),
       http_09_on_non_default_ports_enabled_(
           http_09_on_non_default_ports_enabled) {
   CHECK(connection_) << "ClientSocketHandle passed to HttpBasicState must "
@@ -34,7 +33,6 @@
 HttpBasicState::~HttpBasicState() = default;
 
 void HttpBasicState::Initialize(const HttpRequestInfo* request_info,
-                                bool can_send_early,
                                 RequestPriority priority,
                                 const NetLogWithSource& net_log) {
   DCHECK(!parser_.get());
@@ -46,7 +44,6 @@
       read_buf_.get(), net_log);
   parser_->set_http_09_on_non_default_ports_enabled(
       http_09_on_non_default_ports_enabled_);
-  can_send_early_ = can_send_early;
 }
 
 std::unique_ptr<ClientSocketHandle> HttpBasicState::ReleaseConnection() {
diff --git a/net/http/http_basic_state.h b/net/http/http_basic_state.h
index 58c122ca..d811f9f 100644
--- a/net/http/http_basic_state.h
+++ b/net/http/http_basic_state.h
@@ -35,7 +35,6 @@
 
   // Initialize() must be called before using any of the other methods.
   void Initialize(const HttpRequestInfo* request_info,
-                  bool can_send_early,
                   RequestPriority priority,
                   const NetLogWithSource& net_log);
 
@@ -43,7 +42,6 @@
 
   bool using_proxy() const { return using_proxy_; }
 
-  bool can_send_early() const { return can_send_early_; }
   bool http_09_on_non_default_ports_enabled() const {
     return http_09_on_non_default_ports_enabled_;
   }
@@ -82,8 +80,6 @@
 
   const bool using_proxy_;
 
-  bool can_send_early_;
-
   const bool http_09_on_non_default_ports_enabled_;
 
   GURL url_;
diff --git a/net/http/http_basic_state_unittest.cc b/net/http/http_basic_state_unittest.cc
index dd24b55..a811646 100644
--- a/net/http/http_basic_state_unittest.cc
+++ b/net/http/http_basic_state_unittest.cc
@@ -46,7 +46,7 @@
 TEST(HttpBasicStateTest, InitializeWorks) {
   HttpBasicState state(std::make_unique<ClientSocketHandle>(), false, false);
   const HttpRequestInfo request_info;
-  state.Initialize(&request_info, false, LOW, NetLogWithSource());
+  state.Initialize(&request_info, LOW, NetLogWithSource());
   EXPECT_TRUE(state.parser());
 }
 
@@ -55,7 +55,7 @@
   HttpRequestInfo request_info;
   request_info.traffic_annotation =
       MutableNetworkTrafficAnnotationTag(TRAFFIC_ANNOTATION_FOR_TESTS);
-  state.Initialize(&request_info, false, LOW, NetLogWithSource());
+  state.Initialize(&request_info, LOW, NetLogWithSource());
   EXPECT_EQ(TRAFFIC_ANNOTATION_FOR_TESTS,
             NetworkTrafficAnnotationTag(state.traffic_annotation()));
 }
@@ -63,7 +63,7 @@
 TEST(HttpBasicStateTest, DeleteParser) {
   HttpBasicState state(std::make_unique<ClientSocketHandle>(), false, false);
   const HttpRequestInfo request_info;
-  state.Initialize(&request_info, false, LOW, NetLogWithSource());
+  state.Initialize(&request_info, LOW, NetLogWithSource());
   EXPECT_TRUE(state.parser());
   state.DeleteParser();
   EXPECT_EQ(NULL, state.parser());
@@ -76,7 +76,7 @@
   HttpRequestInfo request_info;
   request_info.url = GURL("http://www.example.com/path?foo=bar#hoge");
   request_info.method = "PUT";
-  state.Initialize(&request_info, false, LOW, NetLogWithSource());
+  state.Initialize(&request_info, LOW, NetLogWithSource());
   EXPECT_EQ("PUT /path?foo=bar HTTP/1.1\r\n", state.GenerateRequestLine());
 }
 
@@ -87,7 +87,7 @@
   HttpRequestInfo request_info;
   request_info.url = GURL("http://www.example.com/path?foo=bar#hoge");
   request_info.method = "PUT";
-  state.Initialize(&request_info, false, LOW, NetLogWithSource());
+  state.Initialize(&request_info, LOW, NetLogWithSource());
   EXPECT_EQ("PUT http://www.example.com/path?foo=bar HTTP/1.1\r\n",
             state.GenerateRequestLine());
 }
diff --git a/net/http/http_basic_stream.cc b/net/http/http_basic_stream.cc
index 9278d66..e516919 100644
--- a/net/http/http_basic_stream.cc
+++ b/net/http/http_basic_stream.cc
@@ -31,8 +31,12 @@
                                       const NetLogWithSource& net_log,
                                       CompletionOnceCallback callback) {
   DCHECK(request_info->traffic_annotation.is_valid());
-  state_.Initialize(request_info, can_send_early, priority, net_log);
-  return OK;
+  state_.Initialize(request_info, priority, net_log);
+  int ret = OK;
+  if (!can_send_early) {
+    ret = parser()->ConfirmHandshake(std::move(callback));
+  }
+  return ret;
 }
 
 int HttpBasicStream::SendRequest(const HttpRequestHeaders& headers,
diff --git a/net/http/http_network_session.cc b/net/http/http_network_session.cc
index 23775ab0..1c32ed9e 100644
--- a/net/http/http_network_session.cc
+++ b/net/http/http_network_session.cc
@@ -95,6 +95,7 @@
       time_func(&base::TimeTicks::Now),
       enable_http2_alternative_service(false),
       enable_websocket_over_http2(false),
+      enable_early_data(false),
       enable_quic(false),
       enable_quic_proxies_for_https_urls(false),
       quic_max_packet_length(quic::kDefaultMaxPacketSize),
@@ -438,6 +439,7 @@
   GetAlpnProtos(&server_config->alpn_protos);
   server_config->ignore_certificate_errors = params_.ignore_certificate_errors;
   *proxy_config = *server_config;
+  server_config->early_data_enabled = params_.enable_early_data;
 }
 
 void HttpNetworkSession::DumpMemoryStats(
diff --git a/net/http/http_network_session.h b/net/http/http_network_session.h
index 2a1a34d..5852772 100644
--- a/net/http/http_network_session.h
+++ b/net/http/http_network_session.h
@@ -127,6 +127,9 @@
     // Whether to enable Websocket over HTTP/2.
     bool enable_websocket_over_http2;
 
+    // Enables 0-RTT support.
+    bool enable_early_data;
+
     // Enables QUIC support.
     bool enable_quic;
 
diff --git a/net/http/http_network_transaction_unittest.cc b/net/http/http_network_transaction_unittest.cc
index 0ee8d86..50fb3e6e 100644
--- a/net/http/http_network_transaction_unittest.cc
+++ b/net/http/http_network_transaction_unittest.cc
@@ -19634,4 +19634,381 @@
 
 #endif  // BUILDFLAG(ENABLE_REPORTING)
 
+TEST_F(HttpNetworkTransactionTest, ZeroRTTDoesntConfirm) {
+  HttpRequestInfo request;
+  request.method = "GET";
+  request.url = GURL("https://www.example.org/");
+  request.traffic_annotation =
+      net::MutableNetworkTrafficAnnotationTag(TRAFFIC_ANNOTATION_FOR_TESTS);
+
+  MockWrite data_writes[] = {
+      MockWrite("GET / HTTP/1.1\r\n"
+                "Host: www.example.org\r\n"
+                "Connection: keep-alive\r\n\r\n"),
+  };
+
+  // The proxy responds to the connect with a 407, using a persistent
+  // connection.
+  MockRead data_reads[] = {
+      MockRead("HTTP/1.1 200 OK\r\n"),
+      MockRead("Content-Length: 1\r\n\r\n"),
+      MockRead(SYNCHRONOUS, "1"),
+  };
+
+  StaticSocketDataProvider data(data_reads, data_writes);
+  session_deps_.socket_factory->AddSocketDataProvider(&data);
+  SSLSocketDataProvider ssl(SYNCHRONOUS, OK);
+  ssl.confirm = MockConfirm(SYNCHRONOUS, OK);
+  session_deps_.enable_early_data = true;
+  session_deps_.socket_factory->AddSSLSocketDataProvider(&ssl);
+
+  std::unique_ptr<HttpNetworkSession> session(CreateSession(&session_deps_));
+
+  TestCompletionCallback callback;
+  auto trans =
+      std::make_unique<HttpNetworkTransaction>(DEFAULT_PRIORITY, session.get());
+
+  int rv = trans->Start(&request, callback.callback(), NetLogWithSource());
+  EXPECT_THAT(rv, IsError(ERR_IO_PENDING));
+
+  rv = callback.WaitForResult();
+  EXPECT_THAT(rv, IsOk());
+
+  const HttpResponseInfo* response = trans->GetResponseInfo();
+  ASSERT_TRUE(response);
+  ASSERT_TRUE(response->headers);
+  EXPECT_EQ(200, response->headers->response_code());
+  EXPECT_EQ(1, response->headers->GetContentLength());
+
+  // Check that ConfirmHandshake wasn't called.
+  ASSERT_FALSE(ssl.ConfirmDataConsumed());
+  ASSERT_TRUE(ssl.WriteBeforeConfirm());
+
+  trans.reset();
+
+  session->CloseAllConnections();
+}
+
+TEST_F(HttpNetworkTransactionTest, ZeroRTTSyncConfirmSyncWrite) {
+  HttpRequestInfo request;
+  request.method = "POST";
+  request.url = GURL("https://www.example.org/");
+  request.traffic_annotation =
+      net::MutableNetworkTrafficAnnotationTag(TRAFFIC_ANNOTATION_FOR_TESTS);
+
+  MockWrite data_writes[] = {
+      MockWrite(SYNCHRONOUS,
+                "POST / HTTP/1.1\r\n"
+                "Host: www.example.org\r\n"
+                "Connection: keep-alive\r\n"
+                "Content-Length: 0\r\n\r\n"),
+  };
+
+  // The proxy responds to the connect with a 407, using a persistent
+  // connection.
+  MockRead data_reads[] = {
+      MockRead("HTTP/1.1 200 OK\r\n"),
+      MockRead("Content-Length: 1\r\n\r\n"),
+      MockRead(SYNCHRONOUS, "1"),
+  };
+
+  StaticSocketDataProvider data(data_reads, data_writes);
+  session_deps_.socket_factory->AddSocketDataProvider(&data);
+  SSLSocketDataProvider ssl(SYNCHRONOUS, OK);
+  ssl.confirm = MockConfirm(SYNCHRONOUS, OK);
+  session_deps_.enable_early_data = true;
+  session_deps_.socket_factory->AddSSLSocketDataProvider(&ssl);
+
+  std::unique_ptr<HttpNetworkSession> session(CreateSession(&session_deps_));
+
+  TestCompletionCallback callback;
+  auto trans =
+      std::make_unique<HttpNetworkTransaction>(DEFAULT_PRIORITY, session.get());
+
+  int rv = trans->Start(&request, callback.callback(), NetLogWithSource());
+  EXPECT_THAT(rv, IsError(ERR_IO_PENDING));
+
+  rv = callback.WaitForResult();
+  EXPECT_THAT(rv, IsOk());
+
+  const HttpResponseInfo* response = trans->GetResponseInfo();
+  ASSERT_TRUE(response);
+  ASSERT_TRUE(response->headers);
+  EXPECT_EQ(200, response->headers->response_code());
+  EXPECT_EQ(1, response->headers->GetContentLength());
+
+  // Check that the Write didn't get called before ConfirmHandshake completed.
+  ASSERT_FALSE(ssl.WriteBeforeConfirm());
+
+  trans.reset();
+
+  session->CloseAllConnections();
+}
+
+TEST_F(HttpNetworkTransactionTest, ZeroRTTSyncConfirmAsyncWrite) {
+  HttpRequestInfo request;
+  request.method = "POST";
+  request.url = GURL("https://www.example.org/");
+  request.traffic_annotation =
+      net::MutableNetworkTrafficAnnotationTag(TRAFFIC_ANNOTATION_FOR_TESTS);
+
+  MockWrite data_writes[] = {
+      MockWrite(ASYNC,
+                "POST / HTTP/1.1\r\n"
+                "Host: www.example.org\r\n"
+                "Connection: keep-alive\r\n"
+                "Content-Length: 0\r\n\r\n"),
+  };
+
+  // The proxy responds to the connect with a 407, using a persistent
+  // connection.
+  MockRead data_reads[] = {
+      MockRead("HTTP/1.1 200 OK\r\n"),
+      MockRead("Content-Length: 1\r\n\r\n"),
+      MockRead(SYNCHRONOUS, "1"),
+  };
+
+  StaticSocketDataProvider data(data_reads, data_writes);
+  session_deps_.socket_factory->AddSocketDataProvider(&data);
+  SSLSocketDataProvider ssl(SYNCHRONOUS, OK);
+  ssl.confirm = MockConfirm(SYNCHRONOUS, OK);
+  session_deps_.enable_early_data = true;
+  session_deps_.socket_factory->AddSSLSocketDataProvider(&ssl);
+
+  std::unique_ptr<HttpNetworkSession> session(CreateSession(&session_deps_));
+
+  TestCompletionCallback callback;
+  auto trans =
+      std::make_unique<HttpNetworkTransaction>(DEFAULT_PRIORITY, session.get());
+
+  int rv = trans->Start(&request, callback.callback(), NetLogWithSource());
+  EXPECT_THAT(rv, IsError(ERR_IO_PENDING));
+
+  rv = callback.WaitForResult();
+  EXPECT_THAT(rv, IsOk());
+
+  const HttpResponseInfo* response = trans->GetResponseInfo();
+  ASSERT_TRUE(response);
+  ASSERT_TRUE(response->headers);
+  EXPECT_EQ(200, response->headers->response_code());
+  EXPECT_EQ(1, response->headers->GetContentLength());
+
+  // Check that the Write didn't get called before ConfirmHandshake completed.
+  ASSERT_FALSE(ssl.WriteBeforeConfirm());
+
+  trans.reset();
+
+  session->CloseAllConnections();
+}
+
+TEST_F(HttpNetworkTransactionTest, ZeroRTTAsyncConfirmSyncWrite) {
+  HttpRequestInfo request;
+  request.method = "POST";
+  request.url = GURL("https://www.example.org/");
+  request.traffic_annotation =
+      net::MutableNetworkTrafficAnnotationTag(TRAFFIC_ANNOTATION_FOR_TESTS);
+
+  MockWrite data_writes[] = {
+      MockWrite(SYNCHRONOUS,
+                "POST / HTTP/1.1\r\n"
+                "Host: www.example.org\r\n"
+                "Connection: keep-alive\r\n"
+                "Content-Length: 0\r\n\r\n"),
+  };
+
+  // The proxy responds to the connect with a 407, using a persistent
+  // connection.
+  MockRead data_reads[] = {
+      MockRead("HTTP/1.1 200 OK\r\n"),
+      MockRead("Content-Length: 1\r\n\r\n"),
+      MockRead(SYNCHRONOUS, "1"),
+  };
+
+  StaticSocketDataProvider data(data_reads, data_writes);
+  session_deps_.socket_factory->AddSocketDataProvider(&data);
+  SSLSocketDataProvider ssl(SYNCHRONOUS, OK);
+  ssl.confirm = MockConfirm(ASYNC, OK);
+  session_deps_.enable_early_data = true;
+  session_deps_.socket_factory->AddSSLSocketDataProvider(&ssl);
+
+  std::unique_ptr<HttpNetworkSession> session(CreateSession(&session_deps_));
+
+  TestCompletionCallback callback;
+  auto trans =
+      std::make_unique<HttpNetworkTransaction>(DEFAULT_PRIORITY, session.get());
+
+  int rv = trans->Start(&request, callback.callback(), NetLogWithSource());
+  EXPECT_THAT(rv, IsError(ERR_IO_PENDING));
+
+  rv = callback.WaitForResult();
+  EXPECT_THAT(rv, IsOk());
+
+  const HttpResponseInfo* response = trans->GetResponseInfo();
+  ASSERT_TRUE(response);
+  ASSERT_TRUE(response->headers);
+  EXPECT_EQ(200, response->headers->response_code());
+  EXPECT_EQ(1, response->headers->GetContentLength());
+
+  // Check that the Write didn't get called before ConfirmHandshake completed.
+  ASSERT_FALSE(ssl.WriteBeforeConfirm());
+
+  trans.reset();
+
+  session->CloseAllConnections();
+}
+
+TEST_F(HttpNetworkTransactionTest, ZeroRTTAsyncConfirmAsyncWrite) {
+  HttpRequestInfo request;
+  request.method = "POST";
+  request.url = GURL("https://www.example.org/");
+  request.traffic_annotation =
+      net::MutableNetworkTrafficAnnotationTag(TRAFFIC_ANNOTATION_FOR_TESTS);
+
+  MockWrite data_writes[] = {
+      MockWrite(ASYNC,
+                "POST / HTTP/1.1\r\n"
+                "Host: www.example.org\r\n"
+                "Connection: keep-alive\r\n"
+                "Content-Length: 0\r\n\r\n"),
+  };
+
+  // The proxy responds to the connect with a 407, using a persistent
+  // connection.
+  MockRead data_reads[] = {
+      MockRead("HTTP/1.1 200 OK\r\n"),
+      MockRead("Content-Length: 1\r\n\r\n"),
+      MockRead(SYNCHRONOUS, "1"),
+  };
+
+  StaticSocketDataProvider data(data_reads, data_writes);
+  session_deps_.socket_factory->AddSocketDataProvider(&data);
+  SSLSocketDataProvider ssl(SYNCHRONOUS, OK);
+  ssl.confirm = MockConfirm(ASYNC, OK);
+  session_deps_.enable_early_data = true;
+  session_deps_.socket_factory->AddSSLSocketDataProvider(&ssl);
+
+  std::unique_ptr<HttpNetworkSession> session(CreateSession(&session_deps_));
+
+  TestCompletionCallback callback;
+  auto trans =
+      std::make_unique<HttpNetworkTransaction>(DEFAULT_PRIORITY, session.get());
+
+  int rv = trans->Start(&request, callback.callback(), NetLogWithSource());
+  EXPECT_THAT(rv, IsError(ERR_IO_PENDING));
+
+  rv = callback.WaitForResult();
+  EXPECT_THAT(rv, IsOk());
+
+  const HttpResponseInfo* response = trans->GetResponseInfo();
+  ASSERT_TRUE(response);
+  ASSERT_TRUE(response->headers);
+  EXPECT_EQ(200, response->headers->response_code());
+  EXPECT_EQ(1, response->headers->GetContentLength());
+
+  // Check that the Write didn't get called before ConfirmHandshake completed.
+  ASSERT_FALSE(ssl.WriteBeforeConfirm());
+
+  trans.reset();
+
+  session->CloseAllConnections();
+}
+
+TEST_F(HttpNetworkTransactionTest, ZeroRTTConfirmErrorSync) {
+  HttpRequestInfo request;
+  request.method = "POST";
+  request.url = GURL("https://www.example.org/");
+  request.traffic_annotation =
+      net::MutableNetworkTrafficAnnotationTag(TRAFFIC_ANNOTATION_FOR_TESTS);
+
+  MockWrite data_writes[] = {
+      MockWrite("POST / HTTP/1.1\r\n"
+                "Host: www.example.org\r\n"
+                "Connection: keep-alive\r\n"
+                "Content-Length: 0\r\n\r\n"),
+  };
+
+  // The proxy responds to the connect with a 407, using a persistent
+  // connection.
+  MockRead data_reads[] = {
+      MockRead("HTTP/1.1 200 OK\r\n"),
+      MockRead("Content-Length: 1\r\n\r\n"),
+      MockRead(SYNCHRONOUS, "1"),
+  };
+
+  StaticSocketDataProvider data(data_reads, data_writes);
+  session_deps_.socket_factory->AddSocketDataProvider(&data);
+  SSLSocketDataProvider ssl(SYNCHRONOUS, OK);
+  ssl.confirm = MockConfirm(SYNCHRONOUS, ERR_SSL_PROTOCOL_ERROR);
+  session_deps_.enable_early_data = true;
+  session_deps_.socket_factory->AddSSLSocketDataProvider(&ssl);
+
+  std::unique_ptr<HttpNetworkSession> session(CreateSession(&session_deps_));
+
+  TestCompletionCallback callback;
+  auto trans =
+      std::make_unique<HttpNetworkTransaction>(DEFAULT_PRIORITY, session.get());
+
+  int rv = trans->Start(&request, callback.callback(), NetLogWithSource());
+  EXPECT_THAT(rv, IsError(ERR_IO_PENDING));
+
+  rv = callback.WaitForResult();
+  EXPECT_THAT(rv, IsError(ERR_SSL_PROTOCOL_ERROR));
+
+  // Check that the Write didn't get called before ConfirmHandshake completed.
+  ASSERT_FALSE(ssl.WriteBeforeConfirm());
+
+  trans.reset();
+
+  session->CloseAllConnections();
+}
+
+TEST_F(HttpNetworkTransactionTest, ZeroRTTConfirmErrorAsync) {
+  HttpRequestInfo request;
+  request.method = "POST";
+  request.url = GURL("https://www.example.org/");
+  request.traffic_annotation =
+      net::MutableNetworkTrafficAnnotationTag(TRAFFIC_ANNOTATION_FOR_TESTS);
+
+  MockWrite data_writes[] = {
+      MockWrite("POST / HTTP/1.1\r\n"
+                "Host: www.example.org\r\n"
+                "Connection: keep-alive\r\n"
+                "Content-Length: 0\r\n\r\n"),
+  };
+
+  // The proxy responds to the connect with a 407, using a persistent
+  // connection.
+  MockRead data_reads[] = {
+      MockRead("HTTP/1.1 200 OK\r\n"),
+      MockRead("Content-Length: 1\r\n\r\n"),
+      MockRead(SYNCHRONOUS, "1"),
+  };
+
+  StaticSocketDataProvider data(data_reads, data_writes);
+  session_deps_.socket_factory->AddSocketDataProvider(&data);
+  SSLSocketDataProvider ssl(SYNCHRONOUS, OK);
+  ssl.confirm = MockConfirm(ASYNC, ERR_SSL_PROTOCOL_ERROR);
+  session_deps_.enable_early_data = true;
+  session_deps_.socket_factory->AddSSLSocketDataProvider(&ssl);
+
+  std::unique_ptr<HttpNetworkSession> session(CreateSession(&session_deps_));
+
+  TestCompletionCallback callback;
+  auto trans =
+      std::make_unique<HttpNetworkTransaction>(DEFAULT_PRIORITY, session.get());
+
+  int rv = trans->Start(&request, callback.callback(), NetLogWithSource());
+  EXPECT_THAT(rv, IsError(ERR_IO_PENDING));
+
+  rv = callback.WaitForResult();
+  EXPECT_THAT(rv, IsError(ERR_SSL_PROTOCOL_ERROR));
+
+  // Check that the Write didn't get called before ConfirmHandshake completed.
+  ASSERT_FALSE(ssl.WriteBeforeConfirm());
+
+  trans.reset();
+
+  session->CloseAllConnections();
+}
+
 }  // namespace net
diff --git a/net/http/http_proxy_connect_job.cc b/net/http/http_proxy_connect_job.cc
index 5792433..a6dd03637 100644
--- a/net/http/http_proxy_connect_job.cc
+++ b/net/http/http_proxy_connect_job.cc
@@ -623,8 +623,9 @@
   spdy_stream_request_ = std::make_unique<SpdyStreamRequest>();
   return spdy_stream_request_->StartRequest(
       SPDY_BIDIRECTIONAL_STREAM, spdy_session,
-      GURL("https://" + params_->endpoint().ToString()), kH2QuicTunnelPriority,
-      socket_tag(), spdy_session->net_log(),
+      GURL("https://" + params_->endpoint().ToString()),
+      false /* no early data */, kH2QuicTunnelPriority, socket_tag(),
+      spdy_session->net_log(),
       base::BindOnce(&HttpProxyConnectJob::OnIOComplete,
                      base::Unretained(this)),
       params_->traffic_annotation());
diff --git a/net/http/http_proxy_connect_job_unittest.cc b/net/http/http_proxy_connect_job_unittest.cc
index c4eb083..dc877737 100644
--- a/net/http/http_proxy_connect_job_unittest.cc
+++ b/net/http/http_proxy_connect_job_unittest.cc
@@ -194,6 +194,8 @@
     session_deps_.socket_factory->AddSocketDataProvider(data_.get());
 
     if (GetParam() != HTTP) {
+      // Keep the old ssl_data in case there is a draining socket.
+      old_ssl_data_.swap(ssl_data_);
       ssl_data_ =
           std::make_unique<SSLSocketDataProvider>(connect_and_ssl_io_mode, OK);
       if (GetParam() == SPDY) {
@@ -241,6 +243,7 @@
   std::unique_ptr<TestProxyDelegate> proxy_delegate_;
 
   std::unique_ptr<SSLSocketDataProvider> ssl_data_;
+  std::unique_ptr<SSLSocketDataProvider> old_ssl_data_;
   std::unique_ptr<SequencedSocketData> data_;
   SpdySessionDependencies session_deps_;
 
diff --git a/net/http/http_stream_parser.cc b/net/http/http_stream_parser.cc
index 3db1d0e..35c7a4e 100644
--- a/net/http/http_stream_parser.cc
+++ b/net/http/http_stream_parser.cc
@@ -320,6 +320,15 @@
   return result > 0 ? OK : result;
 }
 
+int HttpStreamParser::ConfirmHandshake(CompletionOnceCallback callback) {
+  int ret = stream_socket_->ConfirmHandshake(
+      base::BindOnce(&HttpStreamParser::RunConfirmHandshakeCallback,
+                     weak_ptr_factory_.GetWeakPtr()));
+  if (ret == ERR_IO_PENDING)
+    confirm_handshake_callback_ = std::move(callback);
+  return ret;
+}
+
 int HttpStreamParser::ReadResponseHeaders(CompletionOnceCallback callback) {
   DCHECK(io_state_ == STATE_NONE || io_state_ == STATE_DONE);
   DCHECK(callback_.is_null());
@@ -932,6 +941,10 @@
   return OK;
 }
 
+void HttpStreamParser::RunConfirmHandshakeCallback(int rv) {
+  std::move(confirm_handshake_callback_).Run(rv);
+}
+
 int HttpStreamParser::FindAndParseResponseHeaders(int new_bytes) {
   DCHECK_GT(new_bytes, 0);
   DCHECK_EQ(0, read_buf_unused_offset_);
diff --git a/net/http/http_stream_parser.h b/net/http/http_stream_parser.h
index c09a709..16d24e2 100644
--- a/net/http/http_stream_parser.h
+++ b/net/http/http_stream_parser.h
@@ -73,6 +73,8 @@
                   HttpResponseInfo* response,
                   CompletionOnceCallback callback);
 
+  int ConfirmHandshake(CompletionOnceCallback callback);
+
   int ReadResponseHeaders(CompletionOnceCallback callback);
 
   int ReadResponseBody(IOBuffer* buf,
@@ -183,6 +185,8 @@
   // This handles most of the logic for DoReadHeadersComplete.
   int HandleReadHeaderResult(int result);
 
+  void RunConfirmHandshakeCallback(int rv);
+
   // Examines |read_buf_| to find the start and end of the headers. If they are
   // found, parse them with DoParseResponseHeaders().  Return the offset for
   // the end of the headers, or -1 if the complete headers were not found, or
@@ -266,6 +270,9 @@
   scoped_refptr<IOBuffer> user_read_buf_;
   int user_read_buf_len_;
 
+  // The callback to notify a user that the handshake has been confirmed.
+  CompletionOnceCallback confirm_handshake_callback_;
+
   // The callback to notify a user that their request or response is
   // complete or there was an error
   CompletionOnceCallback callback_;
diff --git a/net/socket/socket_test_util.cc b/net/socket/socket_test_util.cc
index 3692ea4..7c949f5 100644
--- a/net/socket/socket_test_util.cc
+++ b/net/socket/socket_test_util.cc
@@ -157,6 +157,12 @@
 
 MockConnect::~MockConnect() = default;
 
+MockConfirm::MockConfirm() : mode(SYNCHRONOUS), result(OK) {}
+
+MockConfirm::MockConfirm(IoMode io_mode, int r) : mode(io_mode), result(r) {}
+
+MockConfirm::~MockConfirm() = default;
+
 bool SocketDataProvider::IsIdle() const {
   return true;
 }
@@ -1461,6 +1467,8 @@
     int buf_len,
     CompletionOnceCallback callback,
     const NetworkTrafficAnnotationTag& traffic_annotation) {
+  if (!data_->is_confirm_data_consumed)
+    data_->write_called_before_confirm = true;
   return stream_socket_->Write(buf, buf_len, std::move(callback),
                                traffic_annotation);
 }
@@ -1486,6 +1494,28 @@
     stream_socket_->Disconnect();
 }
 
+void MockSSLClientSocket::RunConfirmHandshakeCallback(
+    CompletionOnceCallback callback,
+    int result) {
+  data_->is_confirm_data_consumed = true;
+  std::move(callback).Run(result);
+}
+
+int MockSSLClientSocket::ConfirmHandshake(CompletionOnceCallback callback) {
+  DCHECK(stream_socket_->IsConnected());
+  if (data_->is_confirm_data_consumed)
+    return data_->confirm.result;
+  if (data_->confirm.mode == ASYNC) {
+    RunCallbackAsync(
+        base::BindOnce(&MockSSLClientSocket::RunConfirmHandshakeCallback,
+                       base::Unretained(this), std::move(callback)),
+        data_->confirm.result);
+    return ERR_IO_PENDING;
+  }
+  data_->is_confirm_data_consumed = true;
+  return data_->confirm.result;
+}
+
 bool MockSSLClientSocket::IsConnected() const {
   return stream_socket_->IsConnected();
 }
diff --git a/net/socket/socket_test_util.h b/net/socket/socket_test_util.h
index 1b41630..44b8bf7 100644
--- a/net/socket/socket_test_util.h
+++ b/net/socket/socket_test_util.h
@@ -94,6 +94,18 @@
   IPEndPoint peer_addr;
 };
 
+struct MockConfirm {
+  // Asynchronous confirm success.
+  // Creates a MockConfirm with |mode| ASYC and |result| OK.
+  MockConfirm();
+  // Creates a MockConfirm with the specified mode and result.
+  MockConfirm(IoMode io_mode, int r);
+  ~MockConfirm();
+
+  IoMode mode;
+  int result;
+};
+
 // MockRead and MockWrite shares the same interface and members, but we'd like
 // to have distinct types because we don't want to have them used
 // interchangably. To do this, a struct template is defined, and MockRead and
@@ -441,9 +453,18 @@
   // Returns whether MockConnect data has been consumed.
   bool ConnectDataConsumed() const { return is_connect_data_consumed; }
 
+  // Returns whether MockConfirm data has been consumed.
+  bool ConfirmDataConsumed() const { return is_confirm_data_consumed; }
+
+  // Returns whether a Write occurred before ConfirmHandshake completed.
+  bool WriteBeforeConfirm() const { return write_called_before_confirm; }
+
   // Result for Connect().
   MockConnect connect;
 
+  // Result for Confirm().
+  MockConfirm confirm;
+
   // Result for GetNegotiatedProtocol().
   NextProto next_proto;
 
@@ -459,6 +480,8 @@
   uint16_t expected_ssl_version_max;
 
   bool is_connect_data_consumed = false;
+  bool is_confirm_data_consumed = false;
+  bool write_called_before_confirm = false;
 };
 
 // Uses the sequence_number field in the mock reads and writes to
@@ -898,6 +921,7 @@
   // StreamSocket implementation.
   int Connect(CompletionOnceCallback callback) override;
   void Disconnect() override;
+  int ConfirmHandshake(CompletionOnceCallback callback) override;
   bool IsConnected() const override;
   bool IsConnectedAndIdle() const override;
   bool WasEverUsed() const override;
@@ -941,6 +965,8 @@
   void RunCallbackAsync(CompletionOnceCallback callback, int result);
   void RunCallback(CompletionOnceCallback callback, int result);
 
+  void RunConfirmHandshakeCallback(CompletionOnceCallback callback, int result);
+
   bool connected_ = false;
   NetLogWithSource net_log_;
   std::unique_ptr<StreamSocket> stream_socket_;
diff --git a/net/spdy/bidirectional_stream_spdy_impl.cc b/net/spdy/bidirectional_stream_spdy_impl.cc
index f1281455..32d0999 100644
--- a/net/spdy/bidirectional_stream_spdy_impl.cc
+++ b/net/spdy/bidirectional_stream_spdy_impl.cc
@@ -78,7 +78,8 @@
 
   int rv = stream_request_.StartRequest(
       SPDY_BIDIRECTIONAL_STREAM, spdy_session_, request_info_->url,
-      request_info_->priority, request_info_->socket_tag, net_log,
+      false /* no early data */, request_info_->priority,
+      request_info_->socket_tag, net_log,
       base::Bind(&BidirectionalStreamSpdyImpl::OnStreamInitialized,
                  weak_factory_.GetWeakPtr()),
       traffic_annotation);
diff --git a/net/spdy/spdy_http_stream.cc b/net/spdy/spdy_http_stream.cc
index 8d7ea84..63c692a 100644
--- a/net/spdy/spdy_http_stream.cc
+++ b/net/spdy/spdy_http_stream.cc
@@ -156,8 +156,8 @@
   }
 
   int rv = stream_request_.StartRequest(
-      SPDY_REQUEST_RESPONSE_STREAM, spdy_session_, request_info_->url, priority,
-      request_info_->socket_tag, stream_net_log,
+      SPDY_REQUEST_RESPONSE_STREAM, spdy_session_, request_info_->url,
+      can_send_early, priority, request_info_->socket_tag, stream_net_log,
       base::BindOnce(&SpdyHttpStream::OnStreamCreated,
                      weak_factory_.GetWeakPtr(), std::move(callback)),
       NetworkTrafficAnnotationTag(request_info->traffic_annotation));
diff --git a/net/spdy/spdy_network_transaction_unittest.cc b/net/spdy/spdy_network_transaction_unittest.cc
index a17437d95..d95840d 100644
--- a/net/spdy/spdy_network_transaction_unittest.cc
+++ b/net/spdy/spdy_network_transaction_unittest.cc
@@ -8194,4 +8194,540 @@
 
 #endif  // BUILDFLAG(ENABLE_WEBSOCKETS)
 
+TEST_F(SpdyNetworkTransactionTest, ZeroRTTDoesntConfirm) {
+  spdy::SpdySerializedFrame req(
+      spdy_util_.ConstructSpdyGet(nullptr, 0, 1, LOWEST));
+  MockWrite writes[] = {CreateMockWrite(req, 0)};
+
+  spdy::SpdySerializedFrame resp(
+      spdy_util_.ConstructSpdyGetReply(nullptr, 0, 1));
+  spdy::SpdySerializedFrame body(spdy_util_.ConstructSpdyDataFrame(1, true));
+  MockRead reads[] = {
+      CreateMockRead(resp, 1), CreateMockRead(body, 2),
+      MockRead(ASYNC, 0, 3)  // EOF
+  };
+
+  SequencedSocketData data(reads, writes);
+  auto session_deps = std::make_unique<SpdySessionDependencies>();
+  session_deps->enable_early_data = true;
+  NormalSpdyTransactionHelper helper(request_, DEFAULT_PRIORITY, log_,
+                                     std::move(session_deps));
+  auto ssl_provider = std::make_unique<SSLSocketDataProvider>(ASYNC, OK);
+  // Configure |ssl_provider| to fail if ConfirmHandshake is called. The request
+  // should still succeed.
+  ssl_provider->confirm = MockConfirm(SYNCHRONOUS, ERR_SSL_PROTOCOL_ERROR);
+  helper.RunToCompletionWithSSLData(&data, std::move(ssl_provider));
+  TransactionHelperResult out = helper.output();
+  EXPECT_THAT(out.rv, IsOk());
+  EXPECT_EQ("HTTP/1.1 200", out.status_line);
+  EXPECT_EQ("hello!", out.response_data);
+}
+
+// Run multiple concurrent streams that don't require handshake confirmation.
+TEST_F(SpdyNetworkTransactionTest, ZeroRTTNoConfirmMultipleStreams) {
+  spdy::SpdySerializedFrame req1(
+      spdy_util_.ConstructSpdyGet(nullptr, 0, 1, LOWEST));
+  spdy::SpdySerializedFrame req2(
+      spdy_util_.ConstructSpdyGet(nullptr, 0, 3, LOWEST));
+  MockWrite writes1[] = {CreateMockWrite(req1, 0), CreateMockWrite(req2, 3)};
+
+  spdy::SpdySerializedFrame resp1(
+      spdy_util_.ConstructSpdyGetReply(nullptr, 0, 1));
+  spdy::SpdySerializedFrame body1(spdy_util_.ConstructSpdyDataFrame(1, true));
+  spdy::SpdySerializedFrame resp2(
+      spdy_util_.ConstructSpdyGetReply(nullptr, 0, 3));
+  spdy::SpdySerializedFrame body2(spdy_util_.ConstructSpdyDataFrame(3, true));
+  MockRead reads1[] = {
+      CreateMockRead(resp1, 1), CreateMockRead(body1, 2),
+      CreateMockRead(resp2, 4), CreateMockRead(body2, 5),
+      MockRead(ASYNC, 0, 6)  // EOF
+  };
+
+  SequencedSocketData data1(reads1, writes1);
+  SequencedSocketData data2({}, {});
+  auto session_deps = std::make_unique<SpdySessionDependencies>();
+  session_deps->enable_early_data = true;
+  NormalSpdyTransactionHelper helper(request_, DEFAULT_PRIORITY, log_,
+                                     std::move(session_deps));
+  auto ssl_provider1 = std::make_unique<SSLSocketDataProvider>(ASYNC, OK);
+  ssl_provider1->confirm = MockConfirm(SYNCHRONOUS, ERR_SSL_PROTOCOL_ERROR);
+  auto ssl_provider2 = std::make_unique<SSLSocketDataProvider>(ASYNC, OK);
+  ssl_provider2->confirm = MockConfirm(SYNCHRONOUS, ERR_SSL_PROTOCOL_ERROR);
+
+  helper.RunPreTestSetup();
+  helper.AddDataWithSSLSocketDataProvider(&data1, std::move(ssl_provider1));
+  helper.AddDataWithSSLSocketDataProvider(&data2, std::move(ssl_provider2));
+  EXPECT_TRUE(helper.StartDefaultTest());
+
+  HttpNetworkTransaction trans2(DEFAULT_PRIORITY, helper.session());
+  HttpRequestInfo request2;
+  request2.method = "GET";
+  request2.url = GURL(kDefaultUrl);
+  request2.traffic_annotation =
+      net::MutableNetworkTrafficAnnotationTag(TRAFFIC_ANNOTATION_FOR_TESTS);
+  TestCompletionCallback callback2;
+  int rv = trans2.Start(&request2, callback2.callback(), log_);
+  EXPECT_THAT(rv, IsError(ERR_IO_PENDING));
+
+  helper.FinishDefaultTest();
+  EXPECT_THAT(callback2.GetResult(ERR_IO_PENDING), IsOk());
+  helper.VerifyDataConsumed();
+
+  TransactionHelperResult out = helper.output();
+  EXPECT_THAT(out.rv, IsOk());
+  EXPECT_EQ("HTTP/1.1 200", out.status_line);
+  EXPECT_EQ("hello!", out.response_data);
+}
+
+// Run multiple concurrent streams that require handshake confirmation.
+TEST_F(SpdyNetworkTransactionTest, ZeroRTTConfirmMultipleStreams) {
+  spdy::SpdyHeaderBlock req_block1(
+      spdy_util_.ConstructPostHeaderBlock(kDefaultUrl, 0));
+  spdy::SpdySerializedFrame req1(
+      spdy_util_.ConstructSpdyHeaders(1, std::move(req_block1), LOWEST, true));
+  spdy::SpdyHeaderBlock req_block2(
+      spdy_util_.ConstructPostHeaderBlock(kDefaultUrl, 0));
+  spdy::SpdySerializedFrame req2(
+      spdy_util_.ConstructSpdyHeaders(3, std::move(req_block2), LOWEST, true));
+  MockWrite writes[] = {
+      CreateMockWrite(req1, 0),
+      CreateMockWrite(req2, 3),
+  };
+  spdy::SpdySerializedFrame resp1(
+      spdy_util_.ConstructSpdyGetReply(nullptr, 0, 1));
+  spdy::SpdySerializedFrame body1(spdy_util_.ConstructSpdyDataFrame(1, true));
+  spdy::SpdySerializedFrame resp2(
+      spdy_util_.ConstructSpdyGetReply(nullptr, 0, 3));
+  spdy::SpdySerializedFrame body2(spdy_util_.ConstructSpdyDataFrame(3, true));
+  MockRead reads[] = {
+      CreateMockRead(resp1, 1), CreateMockRead(body1, 2),
+      CreateMockRead(resp2, 4), CreateMockRead(body2, 5),
+      MockRead(ASYNC, 0, 6)  // EOF
+  };
+
+  SequencedSocketData data1(reads, writes);
+  SequencedSocketData data2({}, {});
+  UsePostRequest();
+  auto session_deps = std::make_unique<SpdySessionDependencies>();
+  session_deps->enable_early_data = true;
+  NormalSpdyTransactionHelper helper(request_, DEFAULT_PRIORITY, log_,
+                                     std::move(session_deps));
+  auto ssl_provider1 = std::make_unique<SSLSocketDataProvider>(ASYNC, OK);
+  ssl_provider1->confirm = MockConfirm(ASYNC, OK);
+  auto ssl_provider2 = std::make_unique<SSLSocketDataProvider>(ASYNC, OK);
+  ssl_provider2->confirm = MockConfirm(ASYNC, OK);
+
+  helper.RunPreTestSetup();
+  helper.AddDataWithSSLSocketDataProvider(&data1, std::move(ssl_provider1));
+  helper.AddDataWithSSLSocketDataProvider(&data2, std::move(ssl_provider2));
+
+  HttpNetworkTransaction trans1(DEFAULT_PRIORITY, helper.session());
+  HttpRequestInfo request1;
+  request1.method = "POST";
+  request1.url = GURL(kDefaultUrl);
+  request1.traffic_annotation =
+      net::MutableNetworkTrafficAnnotationTag(TRAFFIC_ANNOTATION_FOR_TESTS);
+  TestCompletionCallback callback1;
+  int rv = trans1.Start(&request1, callback1.callback(), log_);
+  EXPECT_THAT(rv, IsError(ERR_IO_PENDING));
+
+  HttpNetworkTransaction trans2(DEFAULT_PRIORITY, helper.session());
+  HttpRequestInfo request2;
+  request2.method = "POST";
+  request2.url = GURL(kDefaultUrl);
+  request2.traffic_annotation =
+      net::MutableNetworkTrafficAnnotationTag(TRAFFIC_ANNOTATION_FOR_TESTS);
+  TestCompletionCallback callback2;
+  rv = trans2.Start(&request2, callback2.callback(), log_);
+  EXPECT_THAT(rv, IsError(ERR_IO_PENDING));
+
+  EXPECT_THAT(callback1.GetResult(ERR_IO_PENDING), IsOk());
+  EXPECT_THAT(callback2.GetResult(ERR_IO_PENDING), IsOk());
+
+  const HttpResponseInfo* response1 = trans1.GetResponseInfo();
+  ASSERT_TRUE(response1);
+  ASSERT_TRUE(response1->headers);
+  EXPECT_EQ(HttpResponseInfo::CONNECTION_INFO_HTTP2,
+            response1->connection_info);
+  EXPECT_EQ("HTTP/1.1 200", response1->headers->GetStatusLine());
+  std::string response_data;
+  ReadTransaction(&trans1, &response_data);
+  EXPECT_EQ("hello!", response_data);
+
+  const HttpResponseInfo* response2 = trans2.GetResponseInfo();
+  ASSERT_TRUE(response2);
+  ASSERT_TRUE(response2->headers);
+  EXPECT_EQ(HttpResponseInfo::CONNECTION_INFO_HTTP2,
+            response2->connection_info);
+  EXPECT_EQ("HTTP/1.1 200", response2->headers->GetStatusLine());
+  ReadTransaction(&trans2, &response_data);
+  EXPECT_EQ("hello!", response_data);
+
+  helper.VerifyDataConsumed();
+}
+
+// Run multiple concurrent streams, the first require a confirmation and the
+// second not requiring confirmation.
+TEST_F(SpdyNetworkTransactionTest, ZeroRTTConfirmNoConfirmStreams) {
+  // This test orders the writes such that the GET (no confirmation) is written
+  // before the POST (confirmation required).
+  spdy::SpdyHeaderBlock req_block1(
+      spdy_util_.ConstructGetHeaderBlock(kDefaultUrl));
+  spdy::SpdySerializedFrame req1(
+      spdy_util_.ConstructSpdyHeaders(1, std::move(req_block1), LOWEST, true));
+  spdy::SpdyHeaderBlock req_block2(
+      spdy_util_.ConstructPostHeaderBlock(kDefaultUrl, 0));
+  spdy::SpdySerializedFrame req2(
+      spdy_util_.ConstructSpdyHeaders(3, std::move(req_block2), LOWEST, true));
+  MockWrite writes[] = {
+      CreateMockWrite(req1, 0),
+      CreateMockWrite(req2, 3),
+  };
+  spdy::SpdySerializedFrame resp1(
+      spdy_util_.ConstructSpdyGetReply(nullptr, 0, 1));
+  spdy::SpdySerializedFrame body1(spdy_util_.ConstructSpdyDataFrame(1, true));
+  spdy::SpdySerializedFrame resp2(
+      spdy_util_.ConstructSpdyGetReply(nullptr, 0, 3));
+  spdy::SpdySerializedFrame body2(spdy_util_.ConstructSpdyDataFrame(3, true));
+  MockRead reads[] = {
+      CreateMockRead(resp1, 1), CreateMockRead(body1, 2),
+      CreateMockRead(resp2, 4), CreateMockRead(body2, 5),
+      MockRead(ASYNC, 0, 6)  // EOF
+  };
+
+  SequencedSocketData data1(reads, writes);
+  SequencedSocketData data2({}, {});
+  UsePostRequest();
+  auto session_deps = std::make_unique<SpdySessionDependencies>();
+  session_deps->enable_early_data = true;
+  NormalSpdyTransactionHelper helper(request_, DEFAULT_PRIORITY, log_,
+                                     std::move(session_deps));
+  auto ssl_provider1 = std::make_unique<SSLSocketDataProvider>(ASYNC, OK);
+  ssl_provider1->confirm = MockConfirm(ASYNC, OK);
+  auto ssl_provider2 = std::make_unique<SSLSocketDataProvider>(ASYNC, OK);
+  ssl_provider2->confirm = MockConfirm(ASYNC, OK);
+
+  helper.RunPreTestSetup();
+  helper.AddDataWithSSLSocketDataProvider(&data1, std::move(ssl_provider1));
+  helper.AddDataWithSSLSocketDataProvider(&data2, std::move(ssl_provider2));
+
+  // TODO(https://crbug.com/949724): Explicitly verify the ordering of
+  // ConfirmHandshake and the second stream.
+
+  HttpNetworkTransaction trans1(DEFAULT_PRIORITY, helper.session());
+  HttpRequestInfo request1;
+  request1.method = "POST";
+  request1.url = GURL(kDefaultUrl);
+  request1.traffic_annotation =
+      net::MutableNetworkTrafficAnnotationTag(TRAFFIC_ANNOTATION_FOR_TESTS);
+  TestCompletionCallback callback1;
+  int rv = trans1.Start(&request1, callback1.callback(), log_);
+  EXPECT_THAT(rv, IsError(ERR_IO_PENDING));
+
+  HttpNetworkTransaction trans2(DEFAULT_PRIORITY, helper.session());
+  HttpRequestInfo request2;
+  request2.method = "GET";
+  request2.url = GURL(kDefaultUrl);
+  request2.traffic_annotation =
+      net::MutableNetworkTrafficAnnotationTag(TRAFFIC_ANNOTATION_FOR_TESTS);
+  TestCompletionCallback callback2;
+  rv = trans2.Start(&request2, callback2.callback(), log_);
+  EXPECT_THAT(rv, IsError(ERR_IO_PENDING));
+
+  EXPECT_THAT(callback1.GetResult(ERR_IO_PENDING), IsOk());
+  EXPECT_THAT(callback2.GetResult(ERR_IO_PENDING), IsOk());
+
+  const HttpResponseInfo* response1 = trans1.GetResponseInfo();
+  ASSERT_TRUE(response1);
+  ASSERT_TRUE(response1->headers);
+  EXPECT_EQ(HttpResponseInfo::CONNECTION_INFO_HTTP2,
+            response1->connection_info);
+  EXPECT_EQ("HTTP/1.1 200", response1->headers->GetStatusLine());
+  std::string response_data;
+  ReadTransaction(&trans1, &response_data);
+  EXPECT_EQ("hello!", response_data);
+
+  const HttpResponseInfo* response2 = trans2.GetResponseInfo();
+  ASSERT_TRUE(response2);
+  ASSERT_TRUE(response2->headers);
+  EXPECT_EQ(HttpResponseInfo::CONNECTION_INFO_HTTP2,
+            response2->connection_info);
+  EXPECT_EQ("HTTP/1.1 200", response2->headers->GetStatusLine());
+  ReadTransaction(&trans2, &response_data);
+  EXPECT_EQ("hello!", response_data);
+
+  helper.VerifyDataConsumed();
+}
+
+// Run multiple concurrent streams, the first not requiring confirmation and the
+// second requiring confirmation.
+TEST_F(SpdyNetworkTransactionTest, ZeroRTTNoConfirmConfirmStreams) {
+  // This test orders the writes such that the GET (no confirmation) is written
+  // before the POST (confirmation required).
+  spdy::SpdyHeaderBlock req_block1(
+      spdy_util_.ConstructGetHeaderBlock(kDefaultUrl));
+  spdy::SpdySerializedFrame req1(
+      spdy_util_.ConstructSpdyHeaders(1, std::move(req_block1), LOWEST, true));
+  spdy::SpdyHeaderBlock req_block2(
+      spdy_util_.ConstructPostHeaderBlock(kDefaultUrl, 0));
+  spdy::SpdySerializedFrame req2(
+      spdy_util_.ConstructSpdyHeaders(3, std::move(req_block2), LOWEST, true));
+  MockWrite writes[] = {
+      CreateMockWrite(req1, 0),
+      CreateMockWrite(req2, 3),
+  };
+  spdy::SpdySerializedFrame resp1(
+      spdy_util_.ConstructSpdyGetReply(nullptr, 0, 1));
+  spdy::SpdySerializedFrame body1(spdy_util_.ConstructSpdyDataFrame(1, true));
+  spdy::SpdySerializedFrame resp2(
+      spdy_util_.ConstructSpdyGetReply(nullptr, 0, 3));
+  spdy::SpdySerializedFrame body2(spdy_util_.ConstructSpdyDataFrame(3, true));
+  MockRead reads[] = {
+      CreateMockRead(resp1, 1), CreateMockRead(body1, 2),
+      CreateMockRead(resp2, 4), CreateMockRead(body2, 5),
+      MockRead(ASYNC, 0, 6)  // EOF
+  };
+
+  SequencedSocketData data1(reads, writes);
+  SequencedSocketData data2({}, {});
+  UsePostRequest();
+  auto session_deps = std::make_unique<SpdySessionDependencies>();
+  session_deps->enable_early_data = true;
+  NormalSpdyTransactionHelper helper(request_, DEFAULT_PRIORITY, log_,
+                                     std::move(session_deps));
+  auto ssl_provider1 = std::make_unique<SSLSocketDataProvider>(ASYNC, OK);
+  ssl_provider1->confirm = MockConfirm(ASYNC, OK);
+  auto ssl_provider2 = std::make_unique<SSLSocketDataProvider>(ASYNC, OK);
+  ssl_provider2->confirm = MockConfirm(ASYNC, OK);
+
+  helper.RunPreTestSetup();
+  helper.AddDataWithSSLSocketDataProvider(&data1, std::move(ssl_provider1));
+  helper.AddDataWithSSLSocketDataProvider(&data2, std::move(ssl_provider2));
+
+  // TODO(https://crbug.com/949724): Explicitly verify the ordering of
+  // ConfirmHandshake and the second stream.
+
+  HttpNetworkTransaction trans1(DEFAULT_PRIORITY, helper.session());
+  HttpRequestInfo request1;
+  request1.method = "GET";
+  request1.url = GURL(kDefaultUrl);
+  request1.traffic_annotation =
+      net::MutableNetworkTrafficAnnotationTag(TRAFFIC_ANNOTATION_FOR_TESTS);
+  TestCompletionCallback callback1;
+  int rv = trans1.Start(&request1, callback1.callback(), log_);
+  EXPECT_THAT(rv, IsError(ERR_IO_PENDING));
+
+  HttpNetworkTransaction trans2(DEFAULT_PRIORITY, helper.session());
+  HttpRequestInfo request2;
+  request2.method = "POST";
+  request2.url = GURL(kDefaultUrl);
+  request2.traffic_annotation =
+      net::MutableNetworkTrafficAnnotationTag(TRAFFIC_ANNOTATION_FOR_TESTS);
+  TestCompletionCallback callback2;
+  rv = trans2.Start(&request2, callback2.callback(), log_);
+  EXPECT_THAT(rv, IsError(ERR_IO_PENDING));
+
+  EXPECT_THAT(callback1.GetResult(ERR_IO_PENDING), IsOk());
+  EXPECT_THAT(callback2.GetResult(ERR_IO_PENDING), IsOk());
+
+  const HttpResponseInfo* response1 = trans1.GetResponseInfo();
+  ASSERT_TRUE(response1);
+  ASSERT_TRUE(response1->headers);
+  EXPECT_EQ(HttpResponseInfo::CONNECTION_INFO_HTTP2,
+            response1->connection_info);
+  EXPECT_EQ("HTTP/1.1 200", response1->headers->GetStatusLine());
+  std::string response_data;
+  ReadTransaction(&trans1, &response_data);
+  EXPECT_EQ("hello!", response_data);
+
+  const HttpResponseInfo* response2 = trans2.GetResponseInfo();
+  ASSERT_TRUE(response2);
+  ASSERT_TRUE(response2->headers);
+  EXPECT_EQ(HttpResponseInfo::CONNECTION_INFO_HTTP2,
+            response2->connection_info);
+  EXPECT_EQ("HTTP/1.1 200", response2->headers->GetStatusLine());
+  ReadTransaction(&trans2, &response_data);
+  EXPECT_EQ("hello!", response_data);
+
+  helper.VerifyDataConsumed();
+}
+
+TEST_F(SpdyNetworkTransactionTest, ZeroRTTSyncConfirmSyncWrite) {
+  spdy::SpdySerializedFrame req(spdy_util_.ConstructSpdyPost(
+      kDefaultUrl, 1, kUploadDataSize, LOWEST, nullptr, 0));
+  spdy::SpdySerializedFrame body(spdy_util_.ConstructSpdyDataFrame(1, true));
+  MockWrite writes[] = {
+      CreateMockWrite(req, 0, SYNCHRONOUS),
+      CreateMockWrite(body, 1),  // POST upload frame
+  };
+
+  spdy::SpdySerializedFrame resp(spdy_util_.ConstructSpdyPostReply(nullptr, 0));
+  MockRead reads[] = {
+      CreateMockRead(resp, 2), CreateMockRead(body, 3),
+      MockRead(ASYNC, 0, 4)  // EOF
+  };
+
+  SequencedSocketData data(reads, writes);
+  UsePostRequest();
+  auto session_deps = std::make_unique<SpdySessionDependencies>();
+  session_deps->enable_early_data = true;
+  NormalSpdyTransactionHelper helper(request_, DEFAULT_PRIORITY, log_,
+                                     std::move(session_deps));
+  auto ssl_provider = std::make_unique<SSLSocketDataProvider>(ASYNC, OK);
+  ssl_provider->confirm = MockConfirm(SYNCHRONOUS, OK);
+  helper.RunToCompletionWithSSLData(&data, std::move(ssl_provider));
+  TransactionHelperResult out = helper.output();
+  EXPECT_THAT(out.rv, IsOk());
+  EXPECT_EQ("HTTP/1.1 200", out.status_line);
+  EXPECT_EQ("hello!", out.response_data);
+}
+
+TEST_F(SpdyNetworkTransactionTest, ZeroRTTSyncConfirmAsyncWrite) {
+  spdy::SpdySerializedFrame req(spdy_util_.ConstructSpdyPost(
+      kDefaultUrl, 1, kUploadDataSize, LOWEST, nullptr, 0));
+  spdy::SpdySerializedFrame body(spdy_util_.ConstructSpdyDataFrame(1, true));
+  MockWrite writes[] = {
+      CreateMockWrite(req, 0, ASYNC),
+      CreateMockWrite(body, 1),  // POST upload frame
+  };
+
+  spdy::SpdySerializedFrame resp(spdy_util_.ConstructSpdyPostReply(nullptr, 0));
+  MockRead reads[] = {
+      CreateMockRead(resp, 2), CreateMockRead(body, 3),
+      MockRead(ASYNC, 0, 4)  // EOF
+  };
+
+  SequencedSocketData data(reads, writes);
+  UsePostRequest();
+  auto session_deps = std::make_unique<SpdySessionDependencies>();
+  session_deps->enable_early_data = true;
+  NormalSpdyTransactionHelper helper(request_, DEFAULT_PRIORITY, log_,
+                                     std::move(session_deps));
+  auto ssl_provider = std::make_unique<SSLSocketDataProvider>(ASYNC, OK);
+  ssl_provider->confirm = MockConfirm(SYNCHRONOUS, OK);
+  helper.RunToCompletionWithSSLData(&data, std::move(ssl_provider));
+  TransactionHelperResult out = helper.output();
+  EXPECT_THAT(out.rv, IsOk());
+  EXPECT_EQ("HTTP/1.1 200", out.status_line);
+  EXPECT_EQ("hello!", out.response_data);
+}
+
+TEST_F(SpdyNetworkTransactionTest, ZeroRTTAsyncConfirmSyncWrite) {
+  spdy::SpdySerializedFrame req(spdy_util_.ConstructSpdyPost(
+      kDefaultUrl, 1, kUploadDataSize, LOWEST, nullptr, 0));
+  spdy::SpdySerializedFrame body(spdy_util_.ConstructSpdyDataFrame(1, true));
+  MockWrite writes[] = {
+      CreateMockWrite(req, 0, SYNCHRONOUS),
+      CreateMockWrite(body, 1),  // POST upload frame
+  };
+
+  spdy::SpdySerializedFrame resp(spdy_util_.ConstructSpdyPostReply(nullptr, 0));
+  MockRead reads[] = {
+      CreateMockRead(resp, 2), CreateMockRead(body, 3),
+      MockRead(ASYNC, 0, 4)  // EOF
+  };
+
+  SequencedSocketData data(reads, writes);
+  UsePostRequest();
+  auto session_deps = std::make_unique<SpdySessionDependencies>();
+  session_deps->enable_early_data = true;
+  NormalSpdyTransactionHelper helper(request_, DEFAULT_PRIORITY, log_,
+                                     std::move(session_deps));
+  auto ssl_provider = std::make_unique<SSLSocketDataProvider>(ASYNC, OK);
+  ssl_provider->confirm = MockConfirm(ASYNC, OK);
+  helper.RunToCompletionWithSSLData(&data, std::move(ssl_provider));
+  TransactionHelperResult out = helper.output();
+  EXPECT_THAT(out.rv, IsOk());
+  EXPECT_EQ("HTTP/1.1 200", out.status_line);
+  EXPECT_EQ("hello!", out.response_data);
+}
+
+TEST_F(SpdyNetworkTransactionTest, ZeroRTTAsyncConfirmAsyncWrite) {
+  spdy::SpdySerializedFrame req(spdy_util_.ConstructSpdyPost(
+      kDefaultUrl, 1, kUploadDataSize, LOWEST, nullptr, 0));
+  spdy::SpdySerializedFrame body(spdy_util_.ConstructSpdyDataFrame(1, true));
+  MockWrite writes[] = {
+      CreateMockWrite(req, 0, ASYNC),
+      CreateMockWrite(body, 1),  // POST upload frame
+  };
+
+  spdy::SpdySerializedFrame resp(spdy_util_.ConstructSpdyPostReply(nullptr, 0));
+  MockRead reads[] = {
+      CreateMockRead(resp, 2), CreateMockRead(body, 3),
+      MockRead(ASYNC, 0, 4)  // EOF
+  };
+
+  SequencedSocketData data(reads, writes);
+  UsePostRequest();
+  auto session_deps = std::make_unique<SpdySessionDependencies>();
+  session_deps->enable_early_data = true;
+  NormalSpdyTransactionHelper helper(request_, DEFAULT_PRIORITY, log_,
+                                     std::move(session_deps));
+  auto ssl_provider = std::make_unique<SSLSocketDataProvider>(ASYNC, OK);
+  ssl_provider->confirm = MockConfirm(ASYNC, OK);
+  helper.RunToCompletionWithSSLData(&data, std::move(ssl_provider));
+  TransactionHelperResult out = helper.output();
+  EXPECT_THAT(out.rv, IsOk());
+  EXPECT_EQ("HTTP/1.1 200", out.status_line);
+  EXPECT_EQ("hello!", out.response_data);
+}
+
+TEST_F(SpdyNetworkTransactionTest, ZeroRTTConfirmErrorSync) {
+  spdy::SpdySerializedFrame req(spdy_util_.ConstructSpdyPost(
+      kDefaultUrl, 1, kUploadDataSize, LOWEST, nullptr, 0));
+  spdy::SpdySerializedFrame body(spdy_util_.ConstructSpdyDataFrame(1, true));
+  MockWrite writes[] = {
+      CreateMockWrite(req, 0), CreateMockWrite(body, 1),  // POST upload frame
+  };
+
+  spdy::SpdySerializedFrame resp(spdy_util_.ConstructSpdyPostReply(nullptr, 0));
+  MockRead reads[] = {
+      CreateMockRead(resp, 2), CreateMockRead(body, 3),
+      MockRead(ASYNC, 0, 4)  // EOF
+  };
+
+  SequencedSocketData data(reads, writes);
+  UsePostRequest();
+  auto session_deps = std::make_unique<SpdySessionDependencies>();
+  session_deps->enable_early_data = true;
+  NormalSpdyTransactionHelper helper(request_, DEFAULT_PRIORITY, log_,
+                                     std::move(session_deps));
+  auto ssl_provider = std::make_unique<SSLSocketDataProvider>(ASYNC, OK);
+  ssl_provider->confirm = MockConfirm(SYNCHRONOUS, ERR_SSL_PROTOCOL_ERROR);
+  helper.RunPreTestSetup();
+  helper.AddDataWithSSLSocketDataProvider(&data, std::move(ssl_provider));
+  helper.RunDefaultTest();
+  TransactionHelperResult out = helper.output();
+  EXPECT_THAT(out.rv, IsError(ERR_SSL_PROTOCOL_ERROR));
+}
+
+TEST_F(SpdyNetworkTransactionTest, ZeroRTTConfirmErrorAsync) {
+  spdy::SpdySerializedFrame req(spdy_util_.ConstructSpdyPost(
+      kDefaultUrl, 1, kUploadDataSize, LOWEST, nullptr, 0));
+  spdy::SpdySerializedFrame body(spdy_util_.ConstructSpdyDataFrame(1, true));
+  MockWrite writes[] = {
+      CreateMockWrite(req, 0), CreateMockWrite(body, 1),  // POST upload frame
+  };
+
+  spdy::SpdySerializedFrame resp(spdy_util_.ConstructSpdyPostReply(nullptr, 0));
+  MockRead reads[] = {
+      CreateMockRead(resp, 2), CreateMockRead(body, 3),
+      MockRead(ASYNC, 0, 4)  // EOF
+  };
+
+  SequencedSocketData data(reads, writes);
+  UsePostRequest();
+  auto session_deps = std::make_unique<SpdySessionDependencies>();
+  session_deps->enable_early_data = true;
+  NormalSpdyTransactionHelper helper(request_, DEFAULT_PRIORITY, log_,
+                                     std::move(session_deps));
+  auto ssl_provider = std::make_unique<SSLSocketDataProvider>(ASYNC, OK);
+  ssl_provider->confirm = MockConfirm(ASYNC, ERR_SSL_PROTOCOL_ERROR);
+  helper.RunPreTestSetup();
+  helper.AddDataWithSSLSocketDataProvider(&data, std::move(ssl_provider));
+  helper.RunDefaultTest();
+  TransactionHelperResult out = helper.output();
+  EXPECT_THAT(out.rv, IsError(ERR_SSL_PROTOCOL_ERROR));
+}
+
 }  // namespace net
diff --git a/net/spdy/spdy_proxy_client_socket_unittest.cc b/net/spdy/spdy_proxy_client_socket_unittest.cc
index 1b09efe..329e2217e 100644
--- a/net/spdy/spdy_proxy_client_socket_unittest.cc
+++ b/net/spdy/spdy_proxy_client_socket_unittest.cc
@@ -199,6 +199,7 @@
   ProxyServer proxy_;
   SpdySessionKey endpoint_spdy_session_key_;
   std::unique_ptr<CommonConnectJobParams> common_connect_job_params_;
+  SSLSocketDataProvider ssl_;
 
   DISALLOW_COPY_AND_ASSIGN(SpdyProxyClientSocketTest);
 };
@@ -215,7 +216,8 @@
                                  proxy_,
                                  PRIVACY_MODE_DISABLED,
                                  SpdySessionKey::IsProxySession::kFalse,
-                                 SocketTag()) {
+                                 SocketTag()),
+      ssl_(SYNCHRONOUS, OK) {
   session_deps_.net_log = net_log_.bound().net_log();
 }
 
@@ -241,11 +243,10 @@
   data_->set_connect_data(connect_data_);
   session_deps_.socket_factory->AddSocketDataProvider(data_.get());
 
-  SSLSocketDataProvider ssl(SYNCHRONOUS, OK);
-  ssl.ssl_info.cert =
+  ssl_.ssl_info.cert =
       ImportCertFromFile(GetTestCertsDirectory(), "spdy_pooling.pem");
-  ASSERT_TRUE(ssl.ssl_info.cert);
-  session_deps_.socket_factory->AddSSLSocketDataProvider(&ssl);
+  ASSERT_TRUE(ssl_.ssl_info.cert);
+  session_deps_.socket_factory->AddSSLSocketDataProvider(&ssl_);
 
   session_ = SpdySessionDependencies::SpdyCreateSession(&session_deps_);
   common_connect_job_params_ = std::make_unique<CommonConnectJobParams>(
diff --git a/net/spdy/spdy_session.cc b/net/spdy/spdy_session.cc
index a88cca7..9b6ac6832 100644
--- a/net/spdy/spdy_session.cc
+++ b/net/spdy/spdy_session.cc
@@ -666,6 +666,7 @@
     SpdyStreamType type,
     const base::WeakPtr<SpdySession>& session,
     const GURL& url,
+    bool can_send_early,
     RequestPriority priority,
     const SocketTag& socket_tag,
     const NetLogWithSource& net_log,
@@ -680,18 +681,25 @@
   type_ = type;
   session_ = session;
   url_ = SimplifyUrlForRequest(url);
+  can_send_early_ = can_send_early;
   priority_ = priority;
   socket_tag_ = socket_tag;
   net_log_ = net_log;
   callback_ = std::move(callback);
   traffic_annotation_ = MutableNetworkTrafficAnnotationTag(traffic_annotation);
 
+  next_state_ = STATE_WAIT_FOR_CONFIRMATION;
+  int rv = DoLoop(OK);
+  if (rv != OK)
+    return rv;
+
   base::WeakPtr<SpdyStream> stream;
-  int rv = session->TryCreateStream(weak_ptr_factory_.GetWeakPtr(), &stream);
-  if (rv == OK) {
-    Reset();
-    stream_ = stream;
-  }
+  rv = session->TryCreateStream(weak_ptr_factory_.GetWeakPtr(), &stream);
+  if (rv != OK)
+    return rv;
+
+  Reset();
+  stream_ = stream;
   return rv;
 }
 
@@ -754,11 +762,74 @@
   session_.reset();
   stream_.reset();
   url_ = GURL();
+  can_send_early_ = false;
   priority_ = MINIMUM_PRIORITY;
   socket_tag_ = SocketTag();
   net_log_ = NetLogWithSource();
   callback_.Reset();
   traffic_annotation_.reset();
+  next_state_ = STATE_NONE;
+}
+
+void SpdyStreamRequest::OnIOComplete(int rv) {
+  if (rv != OK) {
+    OnRequestCompleteFailure(rv);
+  } else {
+    DoLoop(rv);
+  }
+}
+
+int SpdyStreamRequest::DoLoop(int rv) {
+  do {
+    State state = next_state_;
+    next_state_ = STATE_NONE;
+    switch (state) {
+      case STATE_WAIT_FOR_CONFIRMATION:
+        CHECK_EQ(OK, rv);
+        return DoWaitForConfirmation();
+        break;
+      case STATE_REQUEST_STREAM:
+        CHECK_EQ(OK, rv);
+        return DoRequestStream(rv);
+        break;
+      default:
+        NOTREACHED() << "next_state_: " << next_state_;
+        break;
+    }
+  } while (next_state_ != STATE_NONE && next_state_ && rv != ERR_IO_PENDING);
+  return rv;
+}
+
+int SpdyStreamRequest::DoWaitForConfirmation() {
+  if (can_send_early_) {
+    next_state_ = STATE_NONE;
+    return OK;
+  }
+
+  int rv = session_->ConfirmHandshake(base::BindOnce(
+      &SpdyStreamRequest::OnIOComplete, weak_ptr_factory_.GetWeakPtr()));
+  // If ConfirmHandshake returned synchronously, exit the state machine early
+  // so StartRequest can call TryCreateStream synchronously. Otherwise,
+  // TryCreateStream will be called asynchronously as part of the confirmation
+  // state machine.
+  next_state_ = rv == ERR_IO_PENDING ? STATE_REQUEST_STREAM : STATE_NONE;
+  return rv;
+}
+
+int SpdyStreamRequest::DoRequestStream(int rv) {
+  DCHECK_NE(ERR_IO_PENDING, rv);
+  next_state_ = STATE_NONE;
+  if (rv < 0)
+    return rv;
+
+  base::WeakPtr<SpdyStream> stream;
+  rv = session_->TryCreateStream(weak_ptr_factory_.GetWeakPtr(), &stream);
+  if (rv == OK) {
+    OnRequestCompleteSuccess(stream);
+  } else if (rv != ERR_IO_PENDING) {
+    OnRequestCompleteFailure(rv);
+  }
+  return rv;
 }
 
 // static
@@ -854,6 +925,7 @@
       error_on_close_(OK),
       initial_settings_(initial_settings),
       greased_http2_frame_(greased_http2_frame),
+      in_confirm_handshake_(false),
       max_concurrent_streams_(kInitialMaxConcurrentStreams),
       max_concurrent_pushed_streams_(
           initial_settings.at(spdy::SETTINGS_MAX_CONCURRENT_STREAMS)),
@@ -915,6 +987,8 @@
   CHECK(!in_io_loop_);
   DcheckDraining();
 
+  DCHECK(waiting_for_confirmation_callbacks_.empty());
+
   // TODO(akalin): Check connection->is_initialized().
   DCHECK(socket_);
   // With SPDY we can't recycle sockets.
@@ -1033,6 +1107,20 @@
                stream->traffic_annotation());
 }
 
+int SpdySession::ConfirmHandshake(CompletionOnceCallback callback) {
+  int rv = ERR_IO_PENDING;
+  if (!in_confirm_handshake_) {
+    rv = socket_->ConfirmHandshake(
+        base::BindOnce(&SpdySession::NotifyRequestsOfConfirmation,
+                       weak_factory_.GetWeakPtr()));
+  }
+  if (rv == ERR_IO_PENDING) {
+    in_confirm_handshake_ = true;
+    waiting_for_confirmation_callbacks_.push_back(std::move(callback));
+  }
+  return rv;
+}
+
 std::unique_ptr<spdy::SpdySerializedFrame> SpdySession::CreateHeaders(
     spdy::SpdyStreamId stream_id,
     RequestPriority priority,
@@ -2395,6 +2483,15 @@
   return OK;
 }
 
+void SpdySession::NotifyRequestsOfConfirmation(int rv) {
+  for (auto& callback : waiting_for_confirmation_callbacks_) {
+    base::ThreadTaskRunnerHandle::Get()->PostTask(
+        FROM_HERE, base::BindOnce(std::move(callback), rv));
+  }
+  waiting_for_confirmation_callbacks_.clear();
+  in_confirm_handshake_ = false;
+}
+
 void SpdySession::SendInitialData() {
   DCHECK(enable_sending_initial_data_);
   DCHECK(buffered_spdy_framer_.get());
@@ -2784,6 +2881,10 @@
   }
   MakeUnavailable();
 
+  // Notify any requests waiting for handshake confirmation that there is an
+  // error.
+  NotifyRequestsOfConfirmation(err);
+
   // Mark host_port_pair requiring HTTP/1.1 for subsequent connections.
   if (err == ERR_HTTP_1_1_REQUIRED) {
     http_server_properties_->SetHTTP11Required(host_port_pair());
diff --git a/net/spdy/spdy_session.h b/net/spdy/spdy_session.h
index 235c85a..827f1b8 100644
--- a/net/spdy/spdy_session.h
+++ b/net/spdy/spdy_session.h
@@ -203,6 +203,9 @@
   // is not created, an error is returned, and ReleaseStream() may not
   // be called.
   //
+  // If |can_send_early| is true, this request is allowed to be sent over
+  // TLS 1.3 0RTT without confirming the handshake.
+  //
   // If OK is returned, must not be called again without
   // ReleaseStream() being called first. If ERR_IO_PENDING is
   // returned, must not be called again without CancelRequest() or
@@ -211,6 +214,7 @@
   int StartRequest(SpdyStreamType type,
                    const base::WeakPtr<SpdySession>& session,
                    const GURL& url,
+                   bool can_send_early,
                    RequestPriority priority,
                    const SocketTag& socket_tag,
                    const NetLogWithSource& net_log,
@@ -241,6 +245,17 @@
  private:
   friend class SpdySession;
 
+  enum State {
+    STATE_NONE,
+    STATE_WAIT_FOR_CONFIRMATION,
+    STATE_REQUEST_STREAM,
+  };
+
+  void OnIOComplete(int rv);
+  int DoLoop(int rv);
+  int DoWaitForConfirmation();
+  int DoRequestStream(int rv);
+
   // Called by |session_| when the stream attempt has finished
   // successfully.
   void OnRequestCompleteSuccess(const base::WeakPtr<SpdyStream>& stream);
@@ -262,11 +277,13 @@
   base::WeakPtr<SpdySession> session_;
   base::WeakPtr<SpdyStream> stream_;
   GURL url_;
+  bool can_send_early_;
   RequestPriority priority_;
   SocketTag socket_tag_;
   NetLogWithSource net_log_;
   CompletionOnceCallback callback_;
   MutableNetworkTrafficAnnotationTag traffic_annotation_;
+  State next_state_;
 
   base::WeakPtrFactory<SpdyStreamRequest> weak_ptr_factory_;
 
@@ -382,6 +399,11 @@
                           spdy::SpdyFrameType frame_type,
                           std::unique_ptr<SpdyBufferProducer> producer);
 
+  // Runs the handshake to completion to confirm the handshake with the server.
+  // If ERR_IO_PENDING is returned, then when the handshake is confirmed,
+  // |callback| will be called.
+  int ConfirmHandshake(CompletionOnceCallback callback);
+
   // Creates and returns a HEADERS frame for |stream_id|.
   std::unique_ptr<spdy::SpdySerializedFrame> CreateHeaders(
       spdy::SpdyStreamId stream_id,
@@ -704,6 +726,8 @@
   int DoWrite();
   int DoWriteComplete(int result);
 
+  void NotifyRequestsOfConfirmation(int rv);
+
   // TODO(akalin): Rename the Send* and Write* functions below to
   // Enqueue*.
 
@@ -1054,6 +1078,13 @@
   // https://tools.ietf.org/html/draft-bishop-httpbis-grease-00.
   const base::Optional<SpdySessionPool::GreasedHttp2Frame> greased_http2_frame_;
 
+  // The callbacks to notify a request that the handshake has been confirmed.
+  std::vector<CompletionOnceCallback> waiting_for_confirmation_callbacks_;
+
+  // True if there is an ongoing handshake confirmation with outstanding
+  // requests.
+  bool in_confirm_handshake_;
+
   // Limits
   size_t max_concurrent_streams_;
   size_t max_concurrent_pushed_streams_;
diff --git a/net/spdy/spdy_session_fuzzer.cc b/net/spdy/spdy_session_fuzzer.cc
index 4b920299..417cd9a 100644
--- a/net/spdy/spdy_session_fuzzer.cc
+++ b/net/spdy/spdy_session_fuzzer.cc
@@ -131,9 +131,9 @@
   net::TestCompletionCallback wait_for_start;
   int rv = stream_request.StartRequest(
       net::SPDY_REQUEST_RESPONSE_STREAM, spdy_session,
-      GURL("http://www.example.invalid/"), net::DEFAULT_PRIORITY,
-      net::SocketTag(), bound_test_net_log.bound(), wait_for_start.callback(),
-      TRAFFIC_ANNOTATION_FOR_TESTS);
+      GURL("http://www.example.invalid/"), false /* no early data */,
+      net::DEFAULT_PRIORITY, net::SocketTag(), bound_test_net_log.bound(),
+      wait_for_start.callback(), TRAFFIC_ANNOTATION_FOR_TESTS);
 
   if (rv == net::ERR_IO_PENDING) {
     rv = wait_for_start.WaitForResult();
diff --git a/net/spdy/spdy_session_unittest.cc b/net/spdy/spdy_session_unittest.cc
index 05dd8a9..4ee2aee 100644
--- a/net/spdy/spdy_session_unittest.cc
+++ b/net/spdy/spdy_session_unittest.cc
@@ -444,7 +444,7 @@
   StreamRequestDestroyingCallback callback1;
   ASSERT_EQ(ERR_IO_PENDING,
             request1.StartRequest(SPDY_BIDIRECTIONAL_STREAM, session_,
-                                  test_url_, MEDIUM, SocketTag(),
+                                  test_url_, false, MEDIUM, SocketTag(),
                                   NetLogWithSource(), callback1.MakeCallback(),
                                   TRAFFIC_ANNOTATION_FOR_TESTS));
 
@@ -452,7 +452,7 @@
   TestCompletionCallback callback2;
   ASSERT_EQ(ERR_IO_PENDING,
             request2->StartRequest(SPDY_BIDIRECTIONAL_STREAM, session_,
-                                   test_url_, MEDIUM, SocketTag(),
+                                   test_url_, false, MEDIUM, SocketTag(),
                                    NetLogWithSource(), callback2.callback(),
                                    TRAFFIC_ANNOTATION_FOR_TESTS));
 
@@ -879,8 +879,8 @@
 
   SpdyStreamRequest stream_request;
   int rv = stream_request.StartRequest(
-      SPDY_REQUEST_RESPONSE_STREAM, session_, test_url_, MEDIUM, SocketTag(),
-      NetLogWithSource(), CompletionOnceCallback(),
+      SPDY_REQUEST_RESPONSE_STREAM, session_, test_url_, false, MEDIUM,
+      SocketTag(), NetLogWithSource(), CompletionOnceCallback(),
       TRAFFIC_ANNOTATION_FOR_TESTS);
   EXPECT_THAT(rv, IsError(ERR_FAILED));
 
@@ -1305,7 +1305,7 @@
   TestCompletionCallback callback4;
   EXPECT_EQ(ERR_IO_PENDING,
             request4.StartRequest(SPDY_REQUEST_RESPONSE_STREAM, session_,
-                                  test_url_, MEDIUM, SocketTag(),
+                                  test_url_, false, MEDIUM, SocketTag(),
                                   NetLogWithSource(), callback4.callback(),
                                   TRAFFIC_ANNOTATION_FOR_TESTS));
 
@@ -1411,9 +1411,10 @@
   // Start request.
   SpdyStreamRequest request;
   TestCompletionCallback callback;
-  int rv = request.StartRequest(
-      SPDY_REQUEST_RESPONSE_STREAM, session_, test_url_, MEDIUM, SocketTag(),
-      NetLogWithSource(), callback.callback(), TRAFFIC_ANNOTATION_FOR_TESTS);
+  int rv =
+      request.StartRequest(SPDY_REQUEST_RESPONSE_STREAM, session_, test_url_,
+                           false, MEDIUM, SocketTag(), NetLogWithSource(),
+                           callback.callback(), TRAFFIC_ANNOTATION_FOR_TESTS);
   EXPECT_THAT(rv, IsError(ERR_IO_PENDING));
 
   // Stream is stalled.
@@ -1479,7 +1480,7 @@
   TestCompletionCallback callback2;
   EXPECT_EQ(ERR_IO_PENDING,
             request2.StartRequest(SPDY_REQUEST_RESPONSE_STREAM, session_,
-                                  test_url_, MEDIUM, SocketTag(),
+                                  test_url_, false, MEDIUM, SocketTag(),
                                   NetLogWithSource(), callback2.callback(),
                                   TRAFFIC_ANNOTATION_FOR_TESTS));
 
@@ -2000,12 +2001,11 @@
   SpdyStreamRequest request;
   ASSERT_EQ(ERR_IO_PENDING,
             request.StartRequest(SPDY_BIDIRECTIONAL_STREAM, session_, test_url_,
-                                 MEDIUM, SocketTag(), NetLogWithSource(),
+                                 false, MEDIUM, SocketTag(), NetLogWithSource(),
                                  stream_releaser.MakeCallback(&request),
                                  TRAFFIC_ANNOTATION_FOR_TESTS));
 
   base::RunLoop().RunUntilIdle();
-
   EXPECT_THAT(stream_releaser.WaitForResult(), IsOk());
 
   data.Resume();
@@ -2056,7 +2056,7 @@
   SpdyStreamRequest request;
   ASSERT_THAT(
       request.StartRequest(SPDY_BIDIRECTIONAL_STREAM, session_, test_url_,
-                           MEDIUM, SocketTag(), NetLogWithSource(),
+                           false, MEDIUM, SocketTag(), NetLogWithSource(),
                            callback->callback(), TRAFFIC_ANNOTATION_FOR_TESTS),
       IsError(ERR_IO_PENDING));
 
@@ -2089,14 +2089,14 @@
   TestCompletionCallback callback1;
   SpdyStreamRequest request1;
   ASSERT_EQ(OK, request1.StartRequest(SPDY_REQUEST_RESPONSE_STREAM, session_,
-                                      test_url_, LOWEST, SocketTag(),
+                                      test_url_, false, LOWEST, SocketTag(),
                                       NetLogWithSource(), callback1.callback(),
                                       TRAFFIC_ANNOTATION_FOR_TESTS));
   TestCompletionCallback callback2;
   SpdyStreamRequest request2;
   ASSERT_EQ(ERR_IO_PENDING,
             request2.StartRequest(SPDY_REQUEST_RESPONSE_STREAM, session_,
-                                  test_url_, LOWEST, SocketTag(),
+                                  test_url_, false, LOWEST, SocketTag(),
                                   NetLogWithSource(), callback2.callback(),
                                   TRAFFIC_ANNOTATION_FOR_TESTS));
 
@@ -2856,7 +2856,7 @@
   SpdyStreamRequest request2;
   ASSERT_EQ(ERR_IO_PENDING,
             request2.StartRequest(SPDY_REQUEST_RESPONSE_STREAM, session_,
-                                  test_url_, LOWEST, SocketTag(),
+                                  test_url_, false, LOWEST, SocketTag(),
                                   NetLogWithSource(), callback2.callback(),
                                   TRAFFIC_ANNOTATION_FOR_TESTS));
 
@@ -2864,7 +2864,7 @@
   SpdyStreamRequest request3;
   ASSERT_EQ(ERR_IO_PENDING,
             request3.StartRequest(SPDY_REQUEST_RESPONSE_STREAM, session_,
-                                  test_url_, LOWEST, SocketTag(),
+                                  test_url_, false, LOWEST, SocketTag(),
                                   NetLogWithSource(), callback3.callback(),
                                   TRAFFIC_ANNOTATION_FOR_TESTS));
 
@@ -2969,7 +2969,7 @@
   SpdyStreamRequest request2;
   ASSERT_EQ(ERR_IO_PENDING,
             request2.StartRequest(SPDY_BIDIRECTIONAL_STREAM, session_,
-                                  test_url_, LOWEST, SocketTag(),
+                                  test_url_, false, LOWEST, SocketTag(),
                                   NetLogWithSource(), callback2.callback(),
                                   TRAFFIC_ANNOTATION_FOR_TESTS));
 
@@ -2977,7 +2977,7 @@
   SpdyStreamRequest request3;
   ASSERT_EQ(ERR_IO_PENDING,
             request3.StartRequest(SPDY_BIDIRECTIONAL_STREAM, session_,
-                                  test_url_, LOWEST, SocketTag(),
+                                  test_url_, false, LOWEST, SocketTag(),
                                   NetLogWithSource(), callback3.callback(),
                                   TRAFFIC_ANNOTATION_FOR_TESTS));
 
diff --git a/net/spdy/spdy_test_util_common.cc b/net/spdy/spdy_test_util_common.cc
index 0a2d5007..988d82b 100644
--- a/net/spdy/spdy_test_util_common.cc
+++ b/net/spdy/spdy_test_util_common.cc
@@ -251,8 +251,9 @@
     const NetLogWithSource& net_log) {
   SpdyStreamRequest stream_request;
   int rv = stream_request.StartRequest(
-      type, session, url, priority, SocketTag(), net_log,
-      CompletionOnceCallback(), TRAFFIC_ANNOTATION_FOR_TESTS);
+      type, session, url, false /* no early data */, priority, SocketTag(),
+      net_log, CompletionOnceCallback(), TRAFFIC_ANNOTATION_FOR_TESTS);
+
   return
       (rv == OK) ? stream_request.ReleaseStream() : base::WeakPtr<SpdyStream>();
 }
@@ -342,7 +343,8 @@
       enable_websocket_over_http2(false),
       net_log(nullptr),
       http_09_on_non_default_ports_enabled(false),
-      disable_idle_sockets_close_on_memory_pressure(false) {
+      disable_idle_sockets_close_on_memory_pressure(false),
+      enable_early_data(false) {
   http2_settings[spdy::SETTINGS_INITIAL_WINDOW_SIZE] =
       kDefaultInitialWindowSize;
 }
@@ -395,6 +397,7 @@
       session_deps->http_09_on_non_default_ports_enabled;
   params.disable_idle_sockets_close_on_memory_pressure =
       session_deps->disable_idle_sockets_close_on_memory_pressure;
+  params.enable_early_data = session_deps->enable_early_data;
   return params;
 }
 
diff --git a/net/spdy/spdy_test_util_common.h b/net/spdy/spdy_test_util_common.h
index def43e7..e0423d25 100644
--- a/net/spdy/spdy_test_util_common.h
+++ b/net/spdy/spdy_test_util_common.h
@@ -235,6 +235,7 @@
   NetLog* net_log;
   bool http_09_on_non_default_ports_enabled;
   bool disable_idle_sockets_close_on_memory_pressure;
+  bool enable_early_data;
 };
 
 class SpdyURLRequestContext : public URLRequestContext {
diff --git a/net/url_request/url_request_context.h b/net/url_request/url_request_context.h
index d263ebd6..1570b46 100644
--- a/net/url_request/url_request_context.h
+++ b/net/url_request/url_request_context.h
@@ -35,12 +35,6 @@
 }
 }
 
-namespace chromecast {
-namespace shell {
-class URLRequestContextFactory;
-}
-}  // namespace chromecast
-
 namespace safe_browsing {
 class SafeBrowsingURLRequestContextGetter;
 }  // namespace safe_browsing
@@ -311,7 +305,6 @@
   // Whitelist legacy usage of now-deprecated CopyFrom().
   friend class ::ChromeBrowserStateImplIOData;
   friend class ::ProfileImplIOData;
-  friend class chromecast::shell::URLRequestContextFactory;
   friend class safe_browsing::SafeBrowsingURLRequestContextGetter;
 
   // Copies the state from |other| into this context.
diff --git a/net/url_request/url_request_unittest.cc b/net/url_request/url_request_unittest.cc
index a2cde27e..cd8e8de 100644
--- a/net/url_request/url_request_unittest.cc
+++ b/net/url_request/url_request_unittest.cc
@@ -12584,4 +12584,403 @@
 }
 #endif
 
+// Provides a response to the 0RTT request indicating whether it was received
+// as early data.
+class ZeroRTTResponse : public test_server::BasicHttpResponse {
+ public:
+  explicit ZeroRTTResponse(bool zero_rtt) : zero_rtt_(zero_rtt) {}
+  ~ZeroRTTResponse() override {}
+
+  void SendResponse(const test_server::SendBytesCallback& send,
+                    const test_server::SendCompleteCallback& done) override {
+    AddCustomHeader("Vary", "Early-Data");
+    set_content_type("text/plain");
+    AddCustomHeader("Cache-Control", "no-cache");
+    if (zero_rtt_) {
+      set_content("1");
+    } else {
+      set_content("0");
+    }
+
+    // Since the EmbeddedTestServer doesn't keep the socket open by default,
+    // it is explicitly kept alive to allow the remaining leg of the 0RTT
+    // handshake to be received after the early data.
+    send.Run(ToResponseString(), base::DoNothing());
+  }
+
+ private:
+  bool zero_rtt_;
+
+  DISALLOW_COPY_AND_ASSIGN(ZeroRTTResponse);
+};
+
+std::unique_ptr<test_server::HttpResponse> HandleZeroRTTRequest(
+    const test_server::HttpRequest& request) {
+  if (request.GetURL().path() != "/zerortt")
+    return nullptr;
+  auto iter = request.headers.find("Early-Data");
+  bool zero_rtt = iter != request.headers.end() && iter->second == "1";
+  return std::make_unique<ZeroRTTResponse>(zero_rtt);
+}
+
+class HTTPSEarlyDataTest : public TestWithScopedTaskEnvironment {
+ public:
+  HTTPSEarlyDataTest()
+      : context_(true), test_server_(net::EmbeddedTestServer::TYPE_HTTPS) {
+    auto params = std::make_unique<HttpNetworkSession::Params>();
+    params->enable_early_data = true;
+    context_.set_http_network_session_params(std::move(params));
+
+    context_.set_network_delegate(&network_delegate_);
+    cert_verifier_.set_default_result(OK);
+    context_.set_cert_verifier(&cert_verifier_);
+
+    ssl_config_service_ = std::make_unique<TestSSLConfigService>();
+    ssl_config_service_->set_max_version(SSL_PROTOCOL_VERSION_TLS1_3);
+    context_.set_ssl_config_service(ssl_config_service_.get());
+
+    context_.Init();
+
+    ssl_config_.version_max = SSL_PROTOCOL_VERSION_TLS1_3;
+    ssl_config_.early_data_enabled = true;
+    test_server_.SetSSLConfig(net::EmbeddedTestServer::CERT_OK, ssl_config_);
+    test_server_.AddDefaultHandlers(
+        base::FilePath(FILE_PATH_LITERAL("net/data/ssl")));
+    test_server_.RegisterRequestHandler(
+        base::BindRepeating(&HandleZeroRTTRequest));
+  }
+
+  ~HTTPSEarlyDataTest() override = default;
+
+  void ResetSSLConfig(net::EmbeddedTestServer::ServerCertificate cert,
+                      uint16_t version) {
+    ssl_config_.version_max = version;
+    test_server_.ResetSSLConfig(cert, ssl_config_);
+  }
+
+ protected:
+  MockCertVerifier cert_verifier_;
+  TestNetworkDelegate network_delegate_;  // Must outlive URLRequest.
+  std::unique_ptr<TestSSLConfigService> ssl_config_service_;
+  TestURLRequestContext context_;
+
+  SSLServerConfig ssl_config_;
+  EmbeddedTestServer test_server_;
+};
+
+// TLSEarlyDataTest tests that we handle early data correctly.
+TEST_F(HTTPSEarlyDataTest, TLSEarlyDataTest) {
+  ASSERT_TRUE(test_server_.Start());
+  context_.http_transaction_factory()->GetSession()->ClearSSLSessionCache();
+
+  {
+    TestDelegate d;
+    std::unique_ptr<URLRequest> r(context_.CreateRequest(
+        test_server_.GetURL("/zerortt"), DEFAULT_PRIORITY, &d,
+        TRAFFIC_ANNOTATION_FOR_TESTS));
+    r->Start();
+    EXPECT_TRUE(r->is_pending());
+
+    base::RunLoop().Run();
+
+    EXPECT_EQ(1, d.response_started_count());
+
+    EXPECT_EQ(SSL_CONNECTION_VERSION_TLS1_3,
+              SSLConnectionStatusToVersion(r->ssl_info().connection_status));
+    EXPECT_TRUE(r->ssl_info().unverified_cert.get());
+    EXPECT_TRUE(test_server_.GetCertificate()->EqualsIncludingChain(
+        r->ssl_info().cert.get()));
+
+    // The Early-Data header should be omitted in the initial request, and the
+    // handler should return "0".
+    EXPECT_EQ("0", d.data_received());
+  }
+
+  context_.http_transaction_factory()->GetSession()->CloseAllConnections();
+
+  {
+    TestDelegate d;
+    std::unique_ptr<URLRequest> r(context_.CreateRequest(
+        test_server_.GetURL("/zerortt"), DEFAULT_PRIORITY, &d,
+        TRAFFIC_ANNOTATION_FOR_TESTS));
+
+    r->Start();
+    EXPECT_TRUE(r->is_pending());
+
+    base::RunLoop().Run();
+
+    EXPECT_EQ(1, d.response_started_count());
+
+    EXPECT_EQ(SSL_CONNECTION_VERSION_TLS1_3,
+              SSLConnectionStatusToVersion(r->ssl_info().connection_status));
+    EXPECT_TRUE(r->ssl_info().unverified_cert.get());
+    EXPECT_TRUE(test_server_.GetCertificate()->EqualsIncludingChain(
+        r->ssl_info().cert.get()));
+
+    // The Early-Data header should be a single '1' in the resumed request, and
+    // the handler should return "1".
+    EXPECT_EQ("1", d.data_received());
+  }
+}
+
+// TLSEarlyDataTest tests that we handle early data correctly for POST.
+TEST_F(HTTPSEarlyDataTest, TLSEarlyDataPOSTTest) {
+  ASSERT_TRUE(test_server_.Start());
+  context_.http_transaction_factory()->GetSession()->ClearSSLSessionCache();
+
+  {
+    TestDelegate d;
+    std::unique_ptr<URLRequest> r(context_.CreateRequest(
+        test_server_.GetURL("/zerortt"), DEFAULT_PRIORITY, &d,
+        TRAFFIC_ANNOTATION_FOR_TESTS));
+    r->Start();
+    EXPECT_TRUE(r->is_pending());
+
+    base::RunLoop().Run();
+
+    EXPECT_EQ(1, d.response_started_count());
+
+    EXPECT_EQ(SSL_CONNECTION_VERSION_TLS1_3,
+              SSLConnectionStatusToVersion(r->ssl_info().connection_status));
+    EXPECT_TRUE(r->ssl_info().unverified_cert.get());
+    EXPECT_TRUE(test_server_.GetCertificate()->EqualsIncludingChain(
+        r->ssl_info().cert.get()));
+
+    // The Early-Data header should be omitted in the initial request, and the
+    // handler should return "0".
+    EXPECT_EQ("0", d.data_received());
+  }
+
+  context_.http_transaction_factory()->GetSession()->CloseAllConnections();
+
+  {
+    TestDelegate d;
+    std::unique_ptr<URLRequest> r(context_.CreateRequest(
+        test_server_.GetURL("/zerortt"), DEFAULT_PRIORITY, &d,
+        TRAFFIC_ANNOTATION_FOR_TESTS));
+    r->set_method("POST");
+    r->Start();
+    EXPECT_TRUE(r->is_pending());
+
+    base::RunLoop().Run();
+
+    EXPECT_EQ(1, d.response_started_count());
+
+    EXPECT_EQ(SSL_CONNECTION_VERSION_TLS1_3,
+              SSLConnectionStatusToVersion(r->ssl_info().connection_status));
+    EXPECT_TRUE(r->ssl_info().unverified_cert.get());
+    EXPECT_TRUE(test_server_.GetCertificate()->EqualsIncludingChain(
+        r->ssl_info().cert.get()));
+
+    // The Early-Data header should be omitted in the request, since we don't
+    // send POSTs over early data, and the handler should return "0".
+    EXPECT_EQ("0", d.data_received());
+  }
+}
+
+std::unique_ptr<test_server::HttpResponse> HandleTooEarly(
+    bool* sent_425,
+    const test_server::HttpRequest& request) {
+  if (request.GetURL().path() != "/tooearly")
+    return nullptr;
+  std::unique_ptr<test_server::BasicHttpResponse> http_response(
+      new test_server::BasicHttpResponse);
+  http_response->set_content_type("text/html");
+  if (request.headers.find("Early-Data") != request.headers.end()) {
+    EXPECT_EQ("1", request.headers.at("Early-Data"));
+    http_response->set_code(HTTP_TOO_EARLY);
+    http_response->set_content("1");
+    *sent_425 = true;
+  } else {
+    http_response->set_content("None");
+  }
+  return std::move(http_response);
+}
+
+// Test that we handle 425 (Too Early) correctly.
+TEST_F(HTTPSEarlyDataTest, TLSEarlyDataTooEarlyTest) {
+  bool sent_425 = false;
+  test_server_.RegisterRequestHandler(
+      base::BindRepeating(&HandleTooEarly, base::Unretained(&sent_425)));
+  ASSERT_TRUE(test_server_.Start());
+  context_.http_transaction_factory()->GetSession()->ClearSSLSessionCache();
+
+  {
+    TestDelegate d;
+    std::unique_ptr<URLRequest> r(context_.CreateRequest(
+        test_server_.GetURL("/tooearly"), DEFAULT_PRIORITY, &d,
+        TRAFFIC_ANNOTATION_FOR_TESTS));
+    r->Start();
+    EXPECT_TRUE(r->is_pending());
+
+    base::RunLoop().Run();
+
+    EXPECT_EQ(1, d.response_started_count());
+
+    EXPECT_EQ(SSL_CONNECTION_VERSION_TLS1_3,
+              SSLConnectionStatusToVersion(r->ssl_info().connection_status));
+    EXPECT_TRUE(r->ssl_info().unverified_cert.get());
+    EXPECT_TRUE(test_server_.GetCertificate()->EqualsIncludingChain(
+        r->ssl_info().cert.get()));
+
+    // The Early-Data header should be omitted in the initial request, and the
+    // handler should return "None".
+    EXPECT_EQ("None", d.data_received());
+    EXPECT_FALSE(sent_425);
+  }
+
+  context_.http_transaction_factory()->GetSession()->CloseAllConnections();
+
+  {
+    TestDelegate d;
+    std::unique_ptr<URLRequest> r(context_.CreateRequest(
+        test_server_.GetURL("/tooearly"), DEFAULT_PRIORITY, &d,
+        TRAFFIC_ANNOTATION_FOR_TESTS));
+
+    r->Start();
+    EXPECT_TRUE(r->is_pending());
+
+    base::RunLoop().Run();
+
+    EXPECT_EQ(1, d.response_started_count());
+
+    EXPECT_EQ(SSL_CONNECTION_VERSION_TLS1_3,
+              SSLConnectionStatusToVersion(r->ssl_info().connection_status));
+    EXPECT_TRUE(r->ssl_info().unverified_cert.get());
+    EXPECT_TRUE(test_server_.GetCertificate()->EqualsIncludingChain(
+        r->ssl_info().cert.get()));
+
+    // The resumption request will encounter a 425 error and retry without early
+    // data, and the handler should return "None".
+    EXPECT_EQ("None", d.data_received());
+    EXPECT_TRUE(sent_425);
+  }
+}
+
+// TLSEarlyDataRejectTest tests that we gracefully handle an early data reject
+// and retry without early data.
+TEST_F(HTTPSEarlyDataTest, TLSEarlyDataRejectTest) {
+  ASSERT_TRUE(test_server_.Start());
+  context_.http_transaction_factory()->GetSession()->ClearSSLSessionCache();
+
+  {
+    TestDelegate d;
+    std::unique_ptr<URLRequest> r(context_.CreateRequest(
+        test_server_.GetURL("/zerortt"), DEFAULT_PRIORITY, &d,
+        TRAFFIC_ANNOTATION_FOR_TESTS));
+
+    r->Start();
+    EXPECT_TRUE(r->is_pending());
+
+    base::RunLoop().Run();
+
+    EXPECT_EQ(SSL_CONNECTION_VERSION_TLS1_3,
+              SSLConnectionStatusToVersion(r->ssl_info().connection_status));
+    EXPECT_TRUE(r->ssl_info().unverified_cert.get());
+    EXPECT_TRUE(test_server_.GetCertificate()->EqualsIncludingChain(
+        r->ssl_info().cert.get()));
+
+    // The Early-Data header should be omitted in the initial request, and the
+    // handler should return "0".
+    EXPECT_EQ("0", d.data_received());
+  }
+
+  context_.http_transaction_factory()->GetSession()->CloseAllConnections();
+
+  // The certificate in the resumption is changed to confirm that the
+  // certificate change is observed.
+  scoped_refptr<X509Certificate> old_cert = test_server_.GetCertificate();
+  ResetSSLConfig(net::EmbeddedTestServer::CERT_EXPIRED,
+                 SSL_PROTOCOL_VERSION_TLS1_3);
+
+  {
+    TestDelegate d;
+    std::unique_ptr<URLRequest> r(context_.CreateRequest(
+        test_server_.GetURL("/zerortt"), DEFAULT_PRIORITY, &d,
+        TRAFFIC_ANNOTATION_FOR_TESTS));
+
+    r->Start();
+    EXPECT_TRUE(r->is_pending());
+
+    base::RunLoop().Run();
+
+    EXPECT_EQ(1, d.response_started_count());
+
+    EXPECT_EQ(SSL_CONNECTION_VERSION_TLS1_3,
+              SSLConnectionStatusToVersion(r->ssl_info().connection_status));
+    EXPECT_TRUE(r->ssl_info().unverified_cert.get());
+    EXPECT_TRUE(test_server_.GetCertificate()->EqualsIncludingChain(
+        r->ssl_info().cert.get()));
+    EXPECT_FALSE(old_cert->EqualsIncludingChain(r->ssl_info().cert.get()));
+
+    // The Early-Data header should be omitted in the rejected request, and the
+    // handler should return "0".
+    EXPECT_EQ("0", d.data_received());
+  }
+}
+
+// TLSEarlyDataTLS12RejectTest tests that we gracefully handle an early data
+// reject from a TLS 1.2 server and retry without early data.
+TEST_F(HTTPSEarlyDataTest, TLSEarlyDataTLS12RejectTest) {
+  ASSERT_TRUE(test_server_.Start());
+  context_.http_transaction_factory()->GetSession()->ClearSSLSessionCache();
+
+  {
+    TestDelegate d;
+    std::unique_ptr<URLRequest> r(context_.CreateRequest(
+        test_server_.GetURL("/zerortt"), DEFAULT_PRIORITY, &d,
+        TRAFFIC_ANNOTATION_FOR_TESTS));
+
+    r->Start();
+    EXPECT_TRUE(r->is_pending());
+
+    base::RunLoop().Run();
+
+    EXPECT_EQ(1, d.response_started_count());
+
+    EXPECT_EQ(SSL_CONNECTION_VERSION_TLS1_3,
+              SSLConnectionStatusToVersion(r->ssl_info().connection_status));
+    EXPECT_TRUE(r->ssl_info().unverified_cert.get());
+    EXPECT_TRUE(test_server_.GetCertificate()->EqualsIncludingChain(
+        r->ssl_info().cert.get()));
+
+    // The Early-Data header should be omitted in the initial request, and the
+    // handler should return "0".
+    EXPECT_EQ("0", d.data_received());
+  }
+
+  context_.http_transaction_factory()->GetSession()->CloseAllConnections();
+
+  // The certificate in the resumption is changed to confirm that the
+  // certificate change is observed.
+  scoped_refptr<X509Certificate> old_cert = test_server_.GetCertificate();
+  ResetSSLConfig(net::EmbeddedTestServer::CERT_EXPIRED,
+                 SSL_PROTOCOL_VERSION_TLS1_2);
+
+  {
+    TestDelegate d;
+    std::unique_ptr<URLRequest> r(context_.CreateRequest(
+        test_server_.GetURL("/zerortt"), DEFAULT_PRIORITY, &d,
+        TRAFFIC_ANNOTATION_FOR_TESTS));
+
+    r->Start();
+    EXPECT_TRUE(r->is_pending());
+
+    base::RunLoop().Run();
+
+    EXPECT_EQ(1, d.response_started_count());
+
+    EXPECT_EQ(SSL_CONNECTION_VERSION_TLS1_2,
+              SSLConnectionStatusToVersion(r->ssl_info().connection_status));
+    EXPECT_TRUE(r->ssl_info().unverified_cert.get());
+    EXPECT_TRUE(test_server_.GetCertificate()->EqualsIncludingChain(
+        r->ssl_info().cert.get()));
+    EXPECT_FALSE(old_cert->EqualsIncludingChain(r->ssl_info().cert.get()));
+
+    // The Early-Data header should be omitted in the rejected request, and the
+    // handler should return "0".
+    EXPECT_EQ("0", d.data_received());
+  }
+}
+
 }  // namespace net
diff --git a/net/websockets/websocket_basic_handshake_stream.cc b/net/websockets/websocket_basic_handshake_stream.cc
index 9ec81a6..65f63e0 100644
--- a/net/websockets/websocket_basic_handshake_stream.cc
+++ b/net/websockets/websocket_basic_handshake_stream.cc
@@ -203,7 +203,17 @@
     CompletionOnceCallback callback) {
   DCHECK(request_info->traffic_annotation.is_valid());
   url_ = request_info->url;
-  state_.Initialize(request_info, can_send_early, priority, net_log);
+  // The WebSocket may receive a socket in the early data state from
+  // HttpNetworkTransaction, which means it must call ConfirmHandshake() for
+  // requests that need replay protection. However, the first request on any
+  // WebSocket stream is a GET with an idempotent request
+  // (https://tools.ietf.org/html/rfc6455#section-1.3), so there is no need to
+  // call ConfirmHandshake().
+  //
+  // Data after the WebSockets handshake may not be replayable, but the
+  // handshake is guaranteed to be confirmed once the HTTP response is received.
+  DCHECK(can_send_early);
+  state_.Initialize(request_info, priority, net_log);
   return OK;
 }
 
diff --git a/net/websockets/websocket_http2_handshake_stream.cc b/net/websockets/websocket_http2_handshake_stream.cc
index 343b4cd..7e38991 100644
--- a/net/websockets/websocket_http2_handshake_stream.cc
+++ b/net/websockets/websocket_http2_handshake_stream.cc
@@ -121,8 +121,10 @@
 
   callback_ = std::move(callback);
   spdy_stream_request_ = std::make_unique<SpdyStreamRequest>();
+  // The initial request for the WebSocket is a CONNECT, so there is no need to
+  // call ConfirmHandshake().
   int rv = spdy_stream_request_->StartRequest(
-      SPDY_BIDIRECTIONAL_STREAM, session_, request_info_->url, priority_,
+      SPDY_BIDIRECTIONAL_STREAM, session_, request_info_->url, true, priority_,
       request_info_->socket_tag, net_log_,
       base::BindOnce(&WebSocketHttp2HandshakeStream::StartRequestCallback,
                      base::Unretained(this)),
diff --git a/printing/backend/printing_restrictions.cc b/printing/backend/printing_restrictions.cc
index 1274926..038558e8 100644
--- a/printing/backend/printing_restrictions.cc
+++ b/printing/backend/printing_restrictions.cc
@@ -68,11 +68,11 @@
 
 base::Optional<PinModeRestriction> GetPinModeForName(
     const std::string& mode_name) {
-  if (mode_name == "secure")
-    return PinModeRestriction::kSecure;
+  if (mode_name == "pin")
+    return PinModeRestriction::kPin;
 
-  if (mode_name == "unsecure")
-    return PinModeRestriction::kUnsecure;
+  if (mode_name == "no_pin")
+    return PinModeRestriction::kNoPin;
 
   return base::nullopt;
 }
diff --git a/printing/backend/printing_restrictions.h b/printing/backend/printing_restrictions.h
index 1aa80a18..16bb0236 100644
--- a/printing/backend/printing_restrictions.h
+++ b/printing/backend/printing_restrictions.h
@@ -35,8 +35,8 @@
 // This is used in pref file and should never change.
 enum class PinModeRestriction {
   kNone,
-  kSecure,
-  kUnsecure,
+  kPin,
+  kNoPin,
 };
 
 struct PRINTING_EXPORT PrintingRestrictions {
diff --git a/services/device/geolocation/BUILD.gn b/services/device/geolocation/BUILD.gn
index ea8b7c7..49b2827 100644
--- a/services/device/geolocation/BUILD.gn
+++ b/services/device/geolocation/BUILD.gn
@@ -110,7 +110,7 @@
   }
   if (is_chromeos) {
     deps += [
-      "//chromeos/dbus",
+      "//chromeos/dbus/shill",
       "//chromeos/network",
     ]
   }
diff --git a/services/device/geolocation/OWNERS b/services/device/geolocation/OWNERS
index 2ce1e4a..2596b2d 100644
--- a/services/device/geolocation/OWNERS
+++ b/services/device/geolocation/OWNERS
@@ -1,6 +1,8 @@
 mattreynolds@chromium.org
+
+# Original (legacy) owners.
 mcasas@chromium.org
 timvolodine@chromium.org
 
+# COMPONENT: Blink>Geolocation
 # TEAM: device-dev@chromium.org
-# COMPONENT: Blink>Location
diff --git a/services/device/geolocation/geolocation_service_unittest.cc b/services/device/geolocation/geolocation_service_unittest.cc
index 3d69c7a6..36f6ffe 100644
--- a/services/device/geolocation/geolocation_service_unittest.cc
+++ b/services/device/geolocation/geolocation_service_unittest.cc
@@ -7,7 +7,7 @@
 #include "base/test/bind_test_util.h"
 #include "build/build_config.h"
 #if defined(OS_CHROMEOS)
-#include "chromeos/dbus/dbus_thread_manager.h"
+#include "chromeos/dbus/shill/shill_clients.h"
 #include "chromeos/network/geolocation_handler.h"
 #endif
 #include "mojo/public/cpp/bindings/interface_ptr.h"
@@ -40,7 +40,7 @@
  protected:
   void SetUp() override {
 #if defined(OS_CHROMEOS)
-    chromeos::DBusThreadManager::Initialize();
+    chromeos::shill_clients::InitializeFakes();
     chromeos::NetworkHandler::Initialize();
 #endif
     network_change_notifier_.reset(net::NetworkChangeNotifier::CreateMock());
@@ -60,7 +60,7 @@
 
 #if defined(OS_CHROMEOS)
     chromeos::NetworkHandler::Shutdown();
-    chromeos::DBusThreadManager::Shutdown();
+    chromeos::shill_clients::Shutdown();
 #endif
 
     // Let the GeolocationImpl destruct earlier than GeolocationProviderImpl to
diff --git a/services/device/geolocation/wifi_data_provider_chromeos_unittest.cc b/services/device/geolocation/wifi_data_provider_chromeos_unittest.cc
index 757678a..a76e2bdd 100644
--- a/services/device/geolocation/wifi_data_provider_chromeos_unittest.cc
+++ b/services/device/geolocation/wifi_data_provider_chromeos_unittest.cc
@@ -8,7 +8,7 @@
 #include "base/strings/stringprintf.h"
 #include "base/strings/utf_string_conversions.h"
 #include "base/test/scoped_task_environment.h"
-#include "chromeos/dbus/dbus_thread_manager.h"
+#include "chromeos/dbus/shill/shill_clients.h"
 #include "chromeos/dbus/shill/shill_manager_client.h"
 #include "chromeos/network/geolocation_handler.h"
 #include "testing/gtest/include/gtest/gtest.h"
@@ -23,10 +23,9 @@
             base::test::ScopedTaskEnvironment::MainThreadType::UI) {}
 
   void SetUp() override {
-    chromeos::DBusThreadManager::Initialize();
+    chromeos::shill_clients::InitializeFakes();
     chromeos::NetworkHandler::Initialize();
-    manager_client_ =
-        chromeos::DBusThreadManager::Get()->GetShillManagerClient();
+    manager_client_ = chromeos::ShillManagerClient::Get();
     manager_test_ = manager_client_->GetTestInterface();
     provider_ = new WifiDataProviderChromeOs();
     base::RunLoop().RunUntilIdle();
@@ -35,7 +34,7 @@
   void TearDown() override {
     provider_ = NULL;
     chromeos::NetworkHandler::Shutdown();
-    chromeos::DBusThreadManager::Shutdown();
+    chromeos::shill_clients::Shutdown();
   }
 
   bool GetAccessPointData() { return provider_->GetAccessPointData(&ap_data_); }
diff --git a/services/device/time_zone_monitor/time_zone_monitor_fuchsia.cc b/services/device/time_zone_monitor/time_zone_monitor_fuchsia.cc
index ecc955f..63081a2 100644
--- a/services/device/time_zone_monitor/time_zone_monitor_fuchsia.cc
+++ b/services/device/time_zone_monitor/time_zone_monitor_fuchsia.cc
@@ -26,8 +26,7 @@
 // static
 std::unique_ptr<TimeZoneMonitor> TimeZoneMonitor::Create(
     scoped_refptr<base::SequencedTaskRunner> file_task_runner) {
-  // TODO(fuchsia): Implement this. crbug.com/750934
-  NOTIMPLEMENTED();
+  // TODO(https://crbug.com/750934): Implement a real TimeZoneMonitor.
 
   return std::make_unique<TimeZoneMonitorFuchsia>();
 }
diff --git a/services/resource_coordinator/public/mojom/BUILD.gn b/services/resource_coordinator/public/mojom/BUILD.gn
index 2b2c4db..932c3d6 100644
--- a/services/resource_coordinator/public/mojom/BUILD.gn
+++ b/services/resource_coordinator/public/mojom/BUILD.gn
@@ -10,7 +10,6 @@
 
   sources = [
     "coordination_unit.mojom",
-    "coordination_unit_introspector.mojom",
     "lifecycle.mojom",
     "memory_instrumentation/constants.mojom",
     "memory_instrumentation/memory_instrumentation.mojom",
diff --git a/services/resource_coordinator/public/mojom/coordination_unit_introspector.mojom b/services/resource_coordinator/public/mojom/coordination_unit_introspector.mojom
deleted file mode 100644
index 22752bacb..0000000
--- a/services/resource_coordinator/public/mojom/coordination_unit_introspector.mojom
+++ /dev/null
@@ -1,35 +0,0 @@
-// Copyright 2017 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-module resource_coordinator.mojom;
-
-import "services/resource_coordinator/public/mojom/coordination_unit.mojom";
-import "mojo/public/mojom/base/process_id.mojom";
-import "mojo/public/mojom/base/time.mojom";
-
-// A |PageInfo| describes some metrics about a particular page with respect to
-// a given process.
-struct PageInfo {
-  // Identifier to distinguish which URL this |PageInfo| corresponds to.
-  int64 ukm_source_id;
-  // Identifier to distinguish which tab this |PageInfo| corresponds to.
-  uint64 tab_id;
-  // True iff the process for this |PageInfo| hosts the main frame of the page.
-  bool hosts_main_frame;
-  bool is_visible;
-  mojo_base.mojom.TimeDelta time_since_last_navigation;
-  mojo_base.mojom.TimeDelta time_since_last_visibility_change;
-};
-
-struct ProcessInfo {
-  mojo_base.mojom.ProcessId pid;
-  array<PageInfo> page_infos;
-  mojo_base.mojom.Time? launch_time;
-};
-
-interface CoordinationUnitIntrospector {
-  // Returns an array that describes the current topology of Chrome, with
-  // respect to the relationship between processes and hosted frame URLs.
-  GetProcessToURLMap() => (array<ProcessInfo> process_infos);
-};
diff --git a/services/service_manager/sandbox/win/sandbox_win.cc b/services/service_manager/sandbox/win/sandbox_win.cc
index 2d994b72..78e0eb48 100644
--- a/services/service_manager/sandbox/win/sandbox_win.cc
+++ b/services/service_manager/sandbox/win/sandbox_win.cc
@@ -862,13 +862,12 @@
     options.handles_to_inherit = handles_to_inherit;
     BOOL in_job = true;
     // Prior to Windows 8 nested jobs aren't possible.
-    if (sandbox_type == SANDBOX_TYPE_NETWORK &&
-        (base::win::GetVersion() >= base::win::VERSION_WIN8 ||
-         (::IsProcessInJob(::GetCurrentProcess(), nullptr, &in_job) &&
-          !in_job))) {
-      // Launch the process in a job to ensure that the network process doesn't
-      // outlive the browser. This could happen if there is a lot of I/O on
-      // process shutdown, in which case TerminateProcess would fail.
+    if (base::win::GetVersion() >= base::win::VERSION_WIN8 ||
+        (::IsProcessInJob(::GetCurrentProcess(), nullptr, &in_job) &&
+         !in_job)) {
+      // Launch the process in a job to ensure that it doesn't outlive the
+      // browser. This could happen if there is a lot of I/O on process
+      // shutdown, in which case TerminateProcess would fail.
       // https://crbug.com/820996
       if (!g_job_object_handle) {
         sandbox::Job job_obj;
diff --git a/services/shape_detection/OWNERS b/services/shape_detection/OWNERS
index 0e57d5f..7a97495 100644
--- a/services/shape_detection/OWNERS
+++ b/services/shape_detection/OWNERS
@@ -1,5 +1,7 @@
-# COMPONENT: Blink>ImageCapture
-# TEAM: media-dev@chromium.org
-
-mcasas@chromium.org
 reillyg@chromium.org
+
+# Original (legacy) owner.
+mcasas@chromium.org
+
+# COMPONENT: Blink>ImageCapture
+# TEAM: device-dev@chromium.org
diff --git a/services/video_capture/OWNERS b/services/video_capture/OWNERS
index c3dbbd03..495d7a5 100644
--- a/services/video_capture/OWNERS
+++ b/services/video_capture/OWNERS
@@ -1,6 +1,8 @@
-# TEAM: media-capture-and-streams@grotations.appspotmail.com
-# COMPONENT: Blink>GetUserMedia
-
 chfremer@chromium.org
 emircan@chromium.org
-mcasas@chromium.org
+
+# Original (legacy) owner.
+per-file *video*=mcasas@chromium.org
+
+# COMPONENT: Blink>GetUserMedia
+# TEAM: webrtc-dev@chromium.org
diff --git a/testing/buildbot/chromium.gpu.fyi.json b/testing/buildbot/chromium.gpu.fyi.json
index c8ca2e8..a39ec10 100644
--- a/testing/buildbot/chromium.gpu.fyi.json
+++ b/testing/buildbot/chromium.gpu.fyi.json
@@ -1,6 +1,2630 @@
 {
   "AAAAA1 AUTOGENERATED FILE DO NOT EDIT": {},
   "AAAAA2 See generate_buildbot_json.py to make changes": {},
+  "ANGLE GPU Linux Release (Intel HD 630)": {
+    "gtest_tests": [
+      {
+        "args": [
+          "--use-gpu-in-tests",
+          "--test-launcher-retry-limit=0"
+        ],
+        "should_retry_with_patch": false,
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "gpu": "intel-hd-630-ubuntu-stable",
+              "os": "Ubuntu",
+              "pool": "Chrome-GPU"
+            }
+          ],
+          "shards": 4
+        },
+        "test": "angle_end2end_tests"
+      },
+      {
+        "args": [
+          "--use-gpu-in-tests",
+          "--test-launcher-retry-limit=0",
+          "--no-xvfb"
+        ],
+        "should_retry_with_patch": false,
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "gpu": "intel-hd-630-ubuntu-stable",
+              "os": "Ubuntu",
+              "pool": "Chrome-GPU"
+            }
+          ]
+        },
+        "test": "angle_unittests"
+      },
+      {
+        "args": [
+          "--test-launcher-retry-limit=0"
+        ],
+        "should_retry_with_patch": false,
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "gpu": "intel-hd-630-ubuntu-stable",
+              "os": "Ubuntu",
+              "pool": "Chrome-GPU"
+            }
+          ]
+        },
+        "test": "angle_white_box_tests"
+      },
+      {
+        "args": [
+          "--enable-gpu",
+          "--test-launcher-bot-mode",
+          "--test-launcher-jobs=1",
+          "--gtest_filter=CastStreamingApiTestWithPixelOutput.EndToEnd*:TabCaptureApiPixelTest.EndToEnd*",
+          "--no-xvfb"
+        ],
+        "name": "tab_capture_end2end_tests",
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "gpu": "intel-hd-630-ubuntu-stable",
+              "os": "Ubuntu",
+              "pool": "Chrome-GPU"
+            }
+          ]
+        },
+        "test": "browser_tests"
+      },
+      {
+        "args": [
+          "--enable-gpu",
+          "--test-launcher-bot-mode",
+          "--test-launcher-jobs=1",
+          "--test-launcher-filter-file=../../testing/buildbot/filters/vulkan.content_browsertests.filter",
+          "--enable-features=VizDisplayCompositor,UseSkiaRenderer,UiGpuRasterization",
+          "--use-gl=any",
+          "--enable-oop-rasterization",
+          "--enable-vulkan",
+          "--enable-gpu-rasterization",
+          "--enable-raster-to-sk-image",
+          "--force-gpu-rasterization",
+          "--disable-software-compositing-fallback",
+          "--no-xvfb"
+        ],
+        "name": "vulkan_content_browsertests",
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "gpu": "intel-hd-630-ubuntu-stable",
+              "os": "Ubuntu",
+              "pool": "Chrome-GPU"
+            }
+          ]
+        },
+        "test": "content_browsertests"
+      },
+      {
+        "args": [
+          "--use-gpu-in-tests",
+          "--test-launcher-retry-limit=0"
+        ],
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "gpu": "intel-hd-630-ubuntu-stable",
+              "os": "Ubuntu",
+              "pool": "Chrome-GPU"
+            }
+          ],
+          "shards": 4
+        },
+        "test": "dawn_end2end_tests"
+      },
+      {
+        "args": [
+          "--use-gpu-in-tests",
+          "--use-cmd-decoder=validating"
+        ],
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "gpu": "intel-hd-630-ubuntu-stable",
+              "os": "Ubuntu",
+              "pool": "Chrome-GPU"
+            }
+          ]
+        },
+        "test": "gl_tests"
+      },
+      {
+        "args": [
+          "--use-gpu-in-tests",
+          "--no-xvfb"
+        ],
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "gpu": "intel-hd-630-ubuntu-stable",
+              "os": "Ubuntu",
+              "pool": "Chrome-GPU"
+            }
+          ]
+        },
+        "test": "gl_unittests"
+      },
+      {
+        "args": [
+          "--use-gpu-in-tests"
+        ],
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "gpu": "intel-hd-630-ubuntu-stable",
+              "os": "Ubuntu",
+              "pool": "Chrome-GPU"
+            }
+          ]
+        },
+        "test": "gles2_conform_test"
+      },
+      {
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "gpu": "intel-hd-630-ubuntu-stable",
+              "os": "Ubuntu",
+              "pool": "Chrome-GPU"
+            }
+          ]
+        },
+        "test": "swiftshader_unittests"
+      }
+    ],
+    "isolated_scripts": [
+      {
+        "args": [
+          "context_lost",
+          "--show-stdout",
+          "--browser=release",
+          "--passthrough",
+          "-v",
+          "--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc"
+        ],
+        "isolate_name": "telemetry_gpu_integration_test",
+        "name": "context_lost_tests",
+        "should_retry_with_patch": false,
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "gpu": "intel-hd-630-ubuntu-stable",
+              "os": "Ubuntu",
+              "pool": "Chrome-GPU"
+            }
+          ],
+          "idempotent": false
+        }
+      },
+      {
+        "args": [
+          "depth_capture",
+          "--show-stdout",
+          "--browser=release",
+          "--passthrough",
+          "-v",
+          "--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc"
+        ],
+        "isolate_name": "telemetry_gpu_integration_test",
+        "name": "depth_capture_tests",
+        "should_retry_with_patch": false,
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "gpu": "intel-hd-630-ubuntu-stable",
+              "os": "Ubuntu",
+              "pool": "Chrome-GPU"
+            }
+          ],
+          "idempotent": false
+        }
+      },
+      {
+        "args": [
+          "gpu_process",
+          "--show-stdout",
+          "--browser=release",
+          "--passthrough",
+          "-v",
+          "--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc"
+        ],
+        "isolate_name": "telemetry_gpu_integration_test",
+        "name": "gpu_process_launch_tests",
+        "should_retry_with_patch": false,
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "gpu": "intel-hd-630-ubuntu-stable",
+              "os": "Ubuntu",
+              "pool": "Chrome-GPU"
+            }
+          ],
+          "idempotent": false
+        }
+      },
+      {
+        "args": [
+          "hardware_accelerated_feature",
+          "--show-stdout",
+          "--browser=release",
+          "--passthrough",
+          "-v",
+          "--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc"
+        ],
+        "isolate_name": "telemetry_gpu_integration_test",
+        "name": "hardware_accelerated_feature_tests",
+        "should_retry_with_patch": false,
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "gpu": "intel-hd-630-ubuntu-stable",
+              "os": "Ubuntu",
+              "pool": "Chrome-GPU"
+            }
+          ],
+          "idempotent": false
+        }
+      },
+      {
+        "args": [
+          "info_collection",
+          "--show-stdout",
+          "--browser=release",
+          "--passthrough",
+          "-v",
+          "--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc",
+          "--expected-vendor-id",
+          "8086",
+          "--expected-device-id",
+          "5912"
+        ],
+        "isolate_name": "telemetry_gpu_integration_test",
+        "name": "info_collection_tests",
+        "should_retry_with_patch": false,
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "gpu": "intel-hd-630-ubuntu-stable",
+              "os": "Ubuntu",
+              "pool": "Chrome-GPU"
+            }
+          ],
+          "idempotent": false
+        }
+      },
+      {
+        "args": [
+          "maps",
+          "--show-stdout",
+          "--browser=release",
+          "--passthrough",
+          "-v",
+          "--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc",
+          "--dont-restore-color-profile-after-test",
+          "--os-type",
+          "linux",
+          "--build-revision",
+          "${got_revision}",
+          "--test-machine-name",
+          "${buildername}"
+        ],
+        "isolate_name": "telemetry_gpu_integration_test",
+        "name": "maps_pixel_test",
+        "should_retry_with_patch": false,
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "gpu": "intel-hd-630-ubuntu-stable",
+              "os": "Ubuntu",
+              "pool": "Chrome-GPU"
+            }
+          ],
+          "idempotent": false
+        }
+      },
+      {
+        "args": [
+          "pixel",
+          "--show-stdout",
+          "--browser=release",
+          "--passthrough",
+          "-v",
+          "--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc",
+          "--dont-restore-color-profile-after-test",
+          "--refimg-cloud-storage-bucket",
+          "chromium-gpu-archive/reference-images",
+          "--os-type",
+          "linux",
+          "--build-revision",
+          "${got_revision}",
+          "--test-machine-name",
+          "${buildername}",
+          "--use-skia-gold"
+        ],
+        "experiment_percentage": 100,
+        "isolate_name": "telemetry_gpu_integration_test",
+        "name": "pixel_skia_gold_test",
+        "non_precommit_args": [
+          "--upload-refimg-to-cloud-storage"
+        ],
+        "precommit_args": [
+          "--download-refimg-from-cloud-storage",
+          "--review-patch-issue",
+          "${patch_issue}",
+          "--review-patch-set",
+          "${patch_set}",
+          "--buildbucket-build-id",
+          "${buildbucket_build_id}"
+        ],
+        "should_retry_with_patch": false,
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "gpu": "intel-hd-630-ubuntu-stable",
+              "os": "Ubuntu",
+              "pool": "Chrome-GPU"
+            }
+          ],
+          "idempotent": false,
+          "service_account": "chrome-gpu-gold@chops-service-accounts.iam.gserviceaccount.com"
+        }
+      },
+      {
+        "args": [
+          "pixel",
+          "--show-stdout",
+          "--browser=release",
+          "--passthrough",
+          "-v",
+          "--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc",
+          "--dont-restore-color-profile-after-test",
+          "--refimg-cloud-storage-bucket",
+          "chromium-gpu-archive/reference-images",
+          "--os-type",
+          "linux",
+          "--build-revision",
+          "${got_revision}",
+          "--test-machine-name",
+          "${buildername}"
+        ],
+        "isolate_name": "telemetry_gpu_integration_test",
+        "name": "pixel_test",
+        "non_precommit_args": [
+          "--upload-refimg-to-cloud-storage"
+        ],
+        "precommit_args": [
+          "--download-refimg-from-cloud-storage"
+        ],
+        "should_retry_with_patch": false,
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "gpu": "intel-hd-630-ubuntu-stable",
+              "os": "Ubuntu",
+              "pool": "Chrome-GPU"
+            }
+          ],
+          "idempotent": false
+        }
+      },
+      {
+        "args": [
+          "screenshot_sync",
+          "--show-stdout",
+          "--browser=release",
+          "--passthrough",
+          "-v",
+          "--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc",
+          "--dont-restore-color-profile-after-test"
+        ],
+        "isolate_name": "telemetry_gpu_integration_test",
+        "name": "screenshot_sync_tests",
+        "should_retry_with_patch": false,
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "gpu": "intel-hd-630-ubuntu-stable",
+              "os": "Ubuntu",
+              "pool": "Chrome-GPU"
+            }
+          ],
+          "idempotent": false
+        }
+      },
+      {
+        "args": [
+          "trace_test",
+          "--show-stdout",
+          "--browser=release",
+          "--passthrough",
+          "-v",
+          "--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc"
+        ],
+        "isolate_name": "telemetry_gpu_integration_test",
+        "name": "trace_test",
+        "should_retry_with_patch": false,
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "gpu": "intel-hd-630-ubuntu-stable",
+              "os": "Ubuntu",
+              "pool": "Chrome-GPU"
+            }
+          ],
+          "idempotent": false
+        }
+      },
+      {
+        "args": [
+          "webgl_conformance",
+          "--show-stdout",
+          "--browser=release",
+          "--passthrough",
+          "-v",
+          "--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc --use-gl=angle --use-angle=gl --use-cmd-decoder=passthrough",
+          "--webgl-conformance-version=2.0.1",
+          "--read-abbreviated-json-results-from=../../content/test/data/gpu/webgl2_conformance_tests_output.json"
+        ],
+        "isolate_name": "telemetry_gpu_integration_test",
+        "name": "webgl2_conformance_gl_passthrough_tests",
+        "should_retry_with_patch": false,
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "gpu": "intel-hd-630-ubuntu-stable",
+              "os": "Ubuntu",
+              "pool": "Chrome-GPU"
+            }
+          ],
+          "idempotent": false,
+          "shards": 20
+        }
+      },
+      {
+        "args": [
+          "webgl_conformance",
+          "--show-stdout",
+          "--browser=release",
+          "--passthrough",
+          "-v",
+          "--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc",
+          "--webgl-conformance-version=2.0.1",
+          "--read-abbreviated-json-results-from=../../content/test/data/gpu/webgl2_conformance_tests_output.json"
+        ],
+        "isolate_name": "telemetry_gpu_integration_test",
+        "name": "webgl2_conformance_tests",
+        "should_retry_with_patch": false,
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "gpu": "intel-hd-630-ubuntu-stable",
+              "os": "Ubuntu",
+              "pool": "Chrome-GPU"
+            }
+          ],
+          "idempotent": false,
+          "shards": 20
+        }
+      },
+      {
+        "args": [
+          "webgl_conformance",
+          "--show-stdout",
+          "--browser=release",
+          "--passthrough",
+          "-v",
+          "--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc --use-gl=angle --use-angle=gl --use-cmd-decoder=passthrough"
+        ],
+        "isolate_name": "telemetry_gpu_integration_test",
+        "name": "webgl_conformance_gl_passthrough_tests",
+        "should_retry_with_patch": false,
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "gpu": "intel-hd-630-ubuntu-stable",
+              "os": "Ubuntu",
+              "pool": "Chrome-GPU"
+            }
+          ],
+          "idempotent": false,
+          "shards": 2
+        }
+      },
+      {
+        "args": [
+          "webgl_conformance",
+          "--show-stdout",
+          "--browser=release",
+          "--passthrough",
+          "-v",
+          "--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc"
+        ],
+        "isolate_name": "telemetry_gpu_integration_test",
+        "name": "webgl_conformance_tests",
+        "should_retry_with_patch": false,
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "gpu": "intel-hd-630-ubuntu-stable",
+              "os": "Ubuntu",
+              "pool": "Chrome-GPU"
+            }
+          ],
+          "idempotent": false,
+          "shards": 2
+        }
+      }
+    ]
+  },
+  "ANGLE GPU Linux Release (NVIDIA)": {
+    "gtest_tests": [
+      {
+        "args": [
+          "--use-gpu-in-tests",
+          "--test-launcher-retry-limit=0"
+        ],
+        "should_retry_with_patch": false,
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "gpu": "nvidia-quadro-p400-ubuntu-stable",
+              "os": "Ubuntu",
+              "pool": "Chrome-GPU"
+            }
+          ],
+          "shards": 4
+        },
+        "test": "angle_end2end_tests"
+      },
+      {
+        "args": [
+          "--use-gpu-in-tests",
+          "--test-launcher-retry-limit=0",
+          "--no-xvfb"
+        ],
+        "should_retry_with_patch": false,
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "gpu": "nvidia-quadro-p400-ubuntu-stable",
+              "os": "Ubuntu",
+              "pool": "Chrome-GPU"
+            }
+          ]
+        },
+        "test": "angle_unittests"
+      },
+      {
+        "args": [
+          "--test-launcher-retry-limit=0"
+        ],
+        "should_retry_with_patch": false,
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "gpu": "nvidia-quadro-p400-ubuntu-stable",
+              "os": "Ubuntu",
+              "pool": "Chrome-GPU"
+            }
+          ]
+        },
+        "test": "angle_white_box_tests"
+      },
+      {
+        "args": [
+          "--enable-gpu",
+          "--test-launcher-bot-mode",
+          "--test-launcher-jobs=1",
+          "--gtest_filter=CastStreamingApiTestWithPixelOutput.EndToEnd*:TabCaptureApiPixelTest.EndToEnd*",
+          "--no-xvfb"
+        ],
+        "name": "tab_capture_end2end_tests",
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "gpu": "nvidia-quadro-p400-ubuntu-stable",
+              "os": "Ubuntu",
+              "pool": "Chrome-GPU"
+            }
+          ]
+        },
+        "test": "browser_tests"
+      },
+      {
+        "args": [
+          "--enable-gpu",
+          "--test-launcher-bot-mode",
+          "--test-launcher-jobs=1",
+          "--test-launcher-filter-file=../../testing/buildbot/filters/vulkan.content_browsertests.filter",
+          "--enable-features=VizDisplayCompositor,UseSkiaRenderer,UiGpuRasterization",
+          "--use-gl=any",
+          "--enable-oop-rasterization",
+          "--enable-vulkan",
+          "--enable-gpu-rasterization",
+          "--enable-raster-to-sk-image",
+          "--force-gpu-rasterization",
+          "--disable-software-compositing-fallback",
+          "--no-xvfb"
+        ],
+        "name": "vulkan_content_browsertests",
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "gpu": "nvidia-quadro-p400-ubuntu-stable",
+              "os": "Ubuntu",
+              "pool": "Chrome-GPU"
+            }
+          ]
+        },
+        "test": "content_browsertests"
+      },
+      {
+        "args": [
+          "--use-gpu-in-tests",
+          "--test-launcher-retry-limit=0"
+        ],
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "gpu": "nvidia-quadro-p400-ubuntu-stable",
+              "os": "Ubuntu",
+              "pool": "Chrome-GPU"
+            }
+          ],
+          "shards": 4
+        },
+        "test": "dawn_end2end_tests"
+      },
+      {
+        "args": [
+          "--use-gpu-in-tests",
+          "--use-cmd-decoder=validating"
+        ],
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "gpu": "nvidia-quadro-p400-ubuntu-stable",
+              "os": "Ubuntu",
+              "pool": "Chrome-GPU"
+            }
+          ]
+        },
+        "test": "gl_tests"
+      },
+      {
+        "args": [
+          "--use-gpu-in-tests",
+          "--no-xvfb"
+        ],
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "gpu": "nvidia-quadro-p400-ubuntu-stable",
+              "os": "Ubuntu",
+              "pool": "Chrome-GPU"
+            }
+          ]
+        },
+        "test": "gl_unittests"
+      },
+      {
+        "args": [
+          "--use-gpu-in-tests"
+        ],
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "gpu": "nvidia-quadro-p400-ubuntu-stable",
+              "os": "Ubuntu",
+              "pool": "Chrome-GPU"
+            }
+          ]
+        },
+        "test": "gles2_conform_test"
+      },
+      {
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "gpu": "nvidia-quadro-p400-ubuntu-stable",
+              "os": "Ubuntu",
+              "pool": "Chrome-GPU"
+            }
+          ]
+        },
+        "test": "swiftshader_unittests"
+      }
+    ],
+    "isolated_scripts": [
+      {
+        "args": [
+          "--gtest-benchmark-name=angle_perftests",
+          "-v",
+          "--one-frame-only"
+        ],
+        "isolate_name": "angle_perftests",
+        "merge": {
+          "args": [
+            "--smoke-test-mode"
+          ],
+          "script": "//tools/perf/process_perf_results.py"
+        },
+        "name": "angle_perftests",
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "gpu": "nvidia-quadro-p400-ubuntu-stable",
+              "os": "Ubuntu",
+              "pool": "Chrome-GPU"
+            }
+          ]
+        }
+      },
+      {
+        "args": [
+          "context_lost",
+          "--show-stdout",
+          "--browser=release",
+          "--passthrough",
+          "-v",
+          "--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc"
+        ],
+        "isolate_name": "telemetry_gpu_integration_test",
+        "name": "context_lost_tests",
+        "should_retry_with_patch": false,
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "gpu": "nvidia-quadro-p400-ubuntu-stable",
+              "os": "Ubuntu",
+              "pool": "Chrome-GPU"
+            }
+          ],
+          "idempotent": false
+        }
+      },
+      {
+        "args": [
+          "depth_capture",
+          "--show-stdout",
+          "--browser=release",
+          "--passthrough",
+          "-v",
+          "--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc"
+        ],
+        "isolate_name": "telemetry_gpu_integration_test",
+        "name": "depth_capture_tests",
+        "should_retry_with_patch": false,
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "gpu": "nvidia-quadro-p400-ubuntu-stable",
+              "os": "Ubuntu",
+              "pool": "Chrome-GPU"
+            }
+          ],
+          "idempotent": false
+        }
+      },
+      {
+        "args": [
+          "gpu_process",
+          "--show-stdout",
+          "--browser=release",
+          "--passthrough",
+          "-v",
+          "--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc"
+        ],
+        "isolate_name": "telemetry_gpu_integration_test",
+        "name": "gpu_process_launch_tests",
+        "should_retry_with_patch": false,
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "gpu": "nvidia-quadro-p400-ubuntu-stable",
+              "os": "Ubuntu",
+              "pool": "Chrome-GPU"
+            }
+          ],
+          "idempotent": false
+        }
+      },
+      {
+        "args": [
+          "hardware_accelerated_feature",
+          "--show-stdout",
+          "--browser=release",
+          "--passthrough",
+          "-v",
+          "--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc"
+        ],
+        "isolate_name": "telemetry_gpu_integration_test",
+        "name": "hardware_accelerated_feature_tests",
+        "should_retry_with_patch": false,
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "gpu": "nvidia-quadro-p400-ubuntu-stable",
+              "os": "Ubuntu",
+              "pool": "Chrome-GPU"
+            }
+          ],
+          "idempotent": false
+        }
+      },
+      {
+        "args": [
+          "info_collection",
+          "--show-stdout",
+          "--browser=release",
+          "--passthrough",
+          "-v",
+          "--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc",
+          "--expected-vendor-id",
+          "10de",
+          "--expected-device-id",
+          "1cb3"
+        ],
+        "isolate_name": "telemetry_gpu_integration_test",
+        "name": "info_collection_tests",
+        "should_retry_with_patch": false,
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "gpu": "nvidia-quadro-p400-ubuntu-stable",
+              "os": "Ubuntu",
+              "pool": "Chrome-GPU"
+            }
+          ],
+          "idempotent": false
+        }
+      },
+      {
+        "args": [
+          "maps",
+          "--show-stdout",
+          "--browser=release",
+          "--passthrough",
+          "-v",
+          "--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc",
+          "--dont-restore-color-profile-after-test",
+          "--os-type",
+          "linux",
+          "--build-revision",
+          "${got_revision}",
+          "--test-machine-name",
+          "${buildername}"
+        ],
+        "isolate_name": "telemetry_gpu_integration_test",
+        "name": "maps_pixel_test",
+        "should_retry_with_patch": false,
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "gpu": "nvidia-quadro-p400-ubuntu-stable",
+              "os": "Ubuntu",
+              "pool": "Chrome-GPU"
+            }
+          ],
+          "idempotent": false
+        }
+      },
+      {
+        "args": [
+          "pixel",
+          "--show-stdout",
+          "--browser=release",
+          "--passthrough",
+          "-v",
+          "--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc",
+          "--dont-restore-color-profile-after-test",
+          "--refimg-cloud-storage-bucket",
+          "chromium-gpu-archive/reference-images",
+          "--os-type",
+          "linux",
+          "--build-revision",
+          "${got_revision}",
+          "--test-machine-name",
+          "${buildername}",
+          "--use-skia-gold"
+        ],
+        "experiment_percentage": 100,
+        "isolate_name": "telemetry_gpu_integration_test",
+        "name": "pixel_skia_gold_test",
+        "non_precommit_args": [
+          "--upload-refimg-to-cloud-storage"
+        ],
+        "precommit_args": [
+          "--download-refimg-from-cloud-storage",
+          "--review-patch-issue",
+          "${patch_issue}",
+          "--review-patch-set",
+          "${patch_set}",
+          "--buildbucket-build-id",
+          "${buildbucket_build_id}"
+        ],
+        "should_retry_with_patch": false,
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "gpu": "nvidia-quadro-p400-ubuntu-stable",
+              "os": "Ubuntu",
+              "pool": "Chrome-GPU"
+            }
+          ],
+          "idempotent": false,
+          "service_account": "chrome-gpu-gold@chops-service-accounts.iam.gserviceaccount.com"
+        }
+      },
+      {
+        "args": [
+          "pixel",
+          "--show-stdout",
+          "--browser=release",
+          "--passthrough",
+          "-v",
+          "--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc",
+          "--dont-restore-color-profile-after-test",
+          "--refimg-cloud-storage-bucket",
+          "chromium-gpu-archive/reference-images",
+          "--os-type",
+          "linux",
+          "--build-revision",
+          "${got_revision}",
+          "--test-machine-name",
+          "${buildername}"
+        ],
+        "isolate_name": "telemetry_gpu_integration_test",
+        "name": "pixel_test",
+        "non_precommit_args": [
+          "--upload-refimg-to-cloud-storage"
+        ],
+        "precommit_args": [
+          "--download-refimg-from-cloud-storage"
+        ],
+        "should_retry_with_patch": false,
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "gpu": "nvidia-quadro-p400-ubuntu-stable",
+              "os": "Ubuntu",
+              "pool": "Chrome-GPU"
+            }
+          ],
+          "idempotent": false
+        }
+      },
+      {
+        "args": [
+          "screenshot_sync",
+          "--show-stdout",
+          "--browser=release",
+          "--passthrough",
+          "-v",
+          "--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc",
+          "--dont-restore-color-profile-after-test"
+        ],
+        "isolate_name": "telemetry_gpu_integration_test",
+        "name": "screenshot_sync_tests",
+        "should_retry_with_patch": false,
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "gpu": "nvidia-quadro-p400-ubuntu-stable",
+              "os": "Ubuntu",
+              "pool": "Chrome-GPU"
+            }
+          ],
+          "idempotent": false
+        }
+      },
+      {
+        "args": [
+          "trace_test",
+          "--show-stdout",
+          "--browser=release",
+          "--passthrough",
+          "-v",
+          "--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc"
+        ],
+        "isolate_name": "telemetry_gpu_integration_test",
+        "name": "trace_test",
+        "should_retry_with_patch": false,
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "gpu": "nvidia-quadro-p400-ubuntu-stable",
+              "os": "Ubuntu",
+              "pool": "Chrome-GPU"
+            }
+          ],
+          "idempotent": false
+        }
+      },
+      {
+        "args": [
+          "webgl_conformance",
+          "--show-stdout",
+          "--browser=release",
+          "--passthrough",
+          "-v",
+          "--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc --use-gl=angle --use-angle=gl --use-cmd-decoder=passthrough",
+          "--webgl-conformance-version=2.0.1",
+          "--read-abbreviated-json-results-from=../../content/test/data/gpu/webgl2_conformance_tests_output.json"
+        ],
+        "isolate_name": "telemetry_gpu_integration_test",
+        "name": "webgl2_conformance_gl_passthrough_tests",
+        "should_retry_with_patch": false,
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "gpu": "nvidia-quadro-p400-ubuntu-stable",
+              "os": "Ubuntu",
+              "pool": "Chrome-GPU"
+            }
+          ],
+          "idempotent": false,
+          "shards": 20
+        }
+      },
+      {
+        "args": [
+          "webgl_conformance",
+          "--show-stdout",
+          "--browser=release",
+          "--passthrough",
+          "-v",
+          "--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc",
+          "--webgl-conformance-version=2.0.1",
+          "--read-abbreviated-json-results-from=../../content/test/data/gpu/webgl2_conformance_tests_output.json"
+        ],
+        "isolate_name": "telemetry_gpu_integration_test",
+        "name": "webgl2_conformance_tests",
+        "should_retry_with_patch": false,
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "gpu": "nvidia-quadro-p400-ubuntu-stable",
+              "os": "Ubuntu",
+              "pool": "Chrome-GPU"
+            }
+          ],
+          "idempotent": false,
+          "shards": 20
+        }
+      },
+      {
+        "args": [
+          "webgl_conformance",
+          "--show-stdout",
+          "--browser=release",
+          "--passthrough",
+          "-v",
+          "--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc --use-gl=angle --use-angle=gl --use-cmd-decoder=passthrough"
+        ],
+        "isolate_name": "telemetry_gpu_integration_test",
+        "name": "webgl_conformance_gl_passthrough_tests",
+        "should_retry_with_patch": false,
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "gpu": "nvidia-quadro-p400-ubuntu-stable",
+              "os": "Ubuntu",
+              "pool": "Chrome-GPU"
+            }
+          ],
+          "idempotent": false,
+          "shards": 2
+        }
+      },
+      {
+        "args": [
+          "webgl_conformance",
+          "--show-stdout",
+          "--browser=release",
+          "--passthrough",
+          "-v",
+          "--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc"
+        ],
+        "isolate_name": "telemetry_gpu_integration_test",
+        "name": "webgl_conformance_tests",
+        "should_retry_with_patch": false,
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "gpu": "nvidia-quadro-p400-ubuntu-stable",
+              "os": "Ubuntu",
+              "pool": "Chrome-GPU"
+            }
+          ],
+          "idempotent": false,
+          "shards": 2
+        }
+      }
+    ]
+  },
+  "ANGLE GPU Mac Release (Intel)": {
+    "gtest_tests": [
+      {
+        "args": [
+          "--use-gpu-in-tests",
+          "--test-launcher-retry-limit=0"
+        ],
+        "should_retry_with_patch": false,
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "gpu": "8086:0a2e",
+              "os": "Mac-10.12.6"
+            }
+          ],
+          "shards": 4
+        },
+        "test": "angle_end2end_tests"
+      },
+      {
+        "args": [
+          "--use-gpu-in-tests",
+          "--test-launcher-retry-limit=0"
+        ],
+        "should_retry_with_patch": false,
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "gpu": "8086:0a2e",
+              "os": "Mac-10.12.6"
+            }
+          ]
+        },
+        "test": "angle_unittests"
+      },
+      {
+        "args": [
+          "--enable-gpu",
+          "--test-launcher-bot-mode",
+          "--test-launcher-jobs=1",
+          "--gtest_filter=CastStreamingApiTestWithPixelOutput.EndToEnd*:TabCaptureApiPixelTest.EndToEnd*"
+        ],
+        "name": "tab_capture_end2end_tests",
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "gpu": "8086:0a2e",
+              "os": "Mac-10.12.6"
+            }
+          ]
+        },
+        "test": "browser_tests"
+      },
+      {
+        "args": [
+          "--use-gpu-in-tests",
+          "--test-launcher-retry-limit=0"
+        ],
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "gpu": "8086:0a2e",
+              "os": "Mac-10.12.6"
+            }
+          ],
+          "shards": 4
+        },
+        "test": "dawn_end2end_tests"
+      },
+      {
+        "args": [
+          "--use-gpu-in-tests",
+          "--use-cmd-decoder=validating"
+        ],
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "gpu": "8086:0a2e",
+              "os": "Mac-10.12.6"
+            }
+          ]
+        },
+        "test": "gl_tests"
+      },
+      {
+        "args": [
+          "--use-gpu-in-tests"
+        ],
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "gpu": "8086:0a2e",
+              "os": "Mac-10.12.6"
+            }
+          ]
+        },
+        "test": "gl_unittests"
+      },
+      {
+        "args": [
+          "--use-gpu-in-tests"
+        ],
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "gpu": "8086:0a2e",
+              "os": "Mac-10.12.6"
+            }
+          ]
+        },
+        "test": "gles2_conform_test"
+      },
+      {
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "gpu": "8086:0a2e",
+              "os": "Mac-10.12.6"
+            }
+          ]
+        },
+        "test": "gpu_unittests"
+      },
+      {
+        "args": [
+          "--gtest_filter=*Detection*",
+          "--use-gpu-in-tests"
+        ],
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "gpu": "8086:0a2e",
+              "os": "Mac-10.12.6"
+            }
+          ]
+        },
+        "test": "services_unittests"
+      },
+      {
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "gpu": "8086:0a2e",
+              "os": "Mac-10.12.6"
+            }
+          ]
+        },
+        "test": "swiftshader_unittests"
+      }
+    ],
+    "isolated_scripts": [
+      {
+        "args": [
+          "context_lost",
+          "--show-stdout",
+          "--browser=release",
+          "--passthrough",
+          "-v",
+          "--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc"
+        ],
+        "isolate_name": "telemetry_gpu_integration_test",
+        "name": "context_lost_tests",
+        "should_retry_with_patch": false,
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "gpu": "8086:0a2e",
+              "os": "Mac-10.12.6"
+            }
+          ],
+          "idempotent": false
+        }
+      },
+      {
+        "args": [
+          "depth_capture",
+          "--show-stdout",
+          "--browser=release",
+          "--passthrough",
+          "-v",
+          "--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc"
+        ],
+        "isolate_name": "telemetry_gpu_integration_test",
+        "name": "depth_capture_tests",
+        "should_retry_with_patch": false,
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "gpu": "8086:0a2e",
+              "os": "Mac-10.12.6"
+            }
+          ],
+          "idempotent": false
+        }
+      },
+      {
+        "args": [
+          "gpu_process",
+          "--show-stdout",
+          "--browser=release",
+          "--passthrough",
+          "-v",
+          "--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc"
+        ],
+        "isolate_name": "telemetry_gpu_integration_test",
+        "name": "gpu_process_launch_tests",
+        "should_retry_with_patch": false,
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "gpu": "8086:0a2e",
+              "os": "Mac-10.12.6"
+            }
+          ],
+          "idempotent": false
+        }
+      },
+      {
+        "args": [
+          "hardware_accelerated_feature",
+          "--show-stdout",
+          "--browser=release",
+          "--passthrough",
+          "-v",
+          "--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc"
+        ],
+        "isolate_name": "telemetry_gpu_integration_test",
+        "name": "hardware_accelerated_feature_tests",
+        "should_retry_with_patch": false,
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "gpu": "8086:0a2e",
+              "os": "Mac-10.12.6"
+            }
+          ],
+          "idempotent": false
+        }
+      },
+      {
+        "args": [
+          "info_collection",
+          "--show-stdout",
+          "--browser=release",
+          "--passthrough",
+          "-v",
+          "--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc",
+          "--expected-vendor-id",
+          "8086",
+          "--expected-device-id",
+          "0a2e"
+        ],
+        "isolate_name": "telemetry_gpu_integration_test",
+        "name": "info_collection_tests",
+        "should_retry_with_patch": false,
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "gpu": "8086:0a2e",
+              "os": "Mac-10.12.6"
+            }
+          ],
+          "idempotent": false
+        }
+      },
+      {
+        "args": [
+          "maps",
+          "--show-stdout",
+          "--browser=release",
+          "--passthrough",
+          "-v",
+          "--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc",
+          "--dont-restore-color-profile-after-test",
+          "--os-type",
+          "mac",
+          "--build-revision",
+          "${got_revision}",
+          "--test-machine-name",
+          "${buildername}"
+        ],
+        "isolate_name": "telemetry_gpu_integration_test",
+        "name": "maps_pixel_test",
+        "should_retry_with_patch": false,
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "gpu": "8086:0a2e",
+              "os": "Mac-10.12.6"
+            }
+          ],
+          "idempotent": false
+        }
+      },
+      {
+        "args": [
+          "pixel",
+          "--show-stdout",
+          "--browser=release",
+          "--passthrough",
+          "-v",
+          "--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc",
+          "--dont-restore-color-profile-after-test",
+          "--refimg-cloud-storage-bucket",
+          "chromium-gpu-archive/reference-images",
+          "--os-type",
+          "mac",
+          "--build-revision",
+          "${got_revision}",
+          "--test-machine-name",
+          "${buildername}"
+        ],
+        "isolate_name": "telemetry_gpu_integration_test",
+        "name": "pixel_test",
+        "non_precommit_args": [
+          "--upload-refimg-to-cloud-storage"
+        ],
+        "precommit_args": [
+          "--download-refimg-from-cloud-storage"
+        ],
+        "should_retry_with_patch": false,
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "gpu": "8086:0a2e",
+              "os": "Mac-10.12.6"
+            }
+          ],
+          "idempotent": false
+        }
+      },
+      {
+        "args": [
+          "screenshot_sync",
+          "--show-stdout",
+          "--browser=release",
+          "--passthrough",
+          "-v",
+          "--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc",
+          "--dont-restore-color-profile-after-test"
+        ],
+        "isolate_name": "telemetry_gpu_integration_test",
+        "name": "screenshot_sync_tests",
+        "should_retry_with_patch": false,
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "gpu": "8086:0a2e",
+              "os": "Mac-10.12.6"
+            }
+          ],
+          "idempotent": false
+        }
+      },
+      {
+        "args": [
+          "trace_test",
+          "--show-stdout",
+          "--browser=release",
+          "--passthrough",
+          "-v",
+          "--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc"
+        ],
+        "isolate_name": "telemetry_gpu_integration_test",
+        "name": "trace_test",
+        "should_retry_with_patch": false,
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "gpu": "8086:0a2e",
+              "os": "Mac-10.12.6"
+            }
+          ],
+          "idempotent": false
+        }
+      },
+      {
+        "args": [
+          "webgl_conformance",
+          "--show-stdout",
+          "--browser=release",
+          "--passthrough",
+          "-v",
+          "--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc",
+          "--webgl-conformance-version=2.0.1",
+          "--read-abbreviated-json-results-from=../../content/test/data/gpu/webgl2_conformance_tests_output.json"
+        ],
+        "isolate_name": "telemetry_gpu_integration_test",
+        "name": "webgl2_conformance_tests",
+        "should_retry_with_patch": false,
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "gpu": "8086:0a2e",
+              "os": "Mac-10.12.6"
+            }
+          ],
+          "idempotent": false,
+          "shards": 20
+        }
+      },
+      {
+        "args": [
+          "webgl_conformance",
+          "--show-stdout",
+          "--browser=release",
+          "--passthrough",
+          "-v",
+          "--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc"
+        ],
+        "isolate_name": "telemetry_gpu_integration_test",
+        "name": "webgl_conformance_tests",
+        "should_retry_with_patch": false,
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "gpu": "8086:0a2e",
+              "os": "Mac-10.12.6"
+            }
+          ],
+          "idempotent": false,
+          "shards": 2
+        }
+      }
+    ]
+  },
+  "ANGLE GPU Mac Retina Release (AMD)": {
+    "gtest_tests": [
+      {
+        "args": [
+          "--use-gpu-in-tests",
+          "--test-launcher-retry-limit=0"
+        ],
+        "should_retry_with_patch": false,
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "gpu": "1002:6821",
+              "hidpi": "1",
+              "os": "Mac-10.13.6",
+              "pool": "Chrome-GPU"
+            }
+          ],
+          "shards": 4
+        },
+        "test": "angle_end2end_tests"
+      },
+      {
+        "args": [
+          "--use-gpu-in-tests",
+          "--test-launcher-retry-limit=0"
+        ],
+        "should_retry_with_patch": false,
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "gpu": "1002:6821",
+              "hidpi": "1",
+              "os": "Mac-10.13.6",
+              "pool": "Chrome-GPU"
+            }
+          ]
+        },
+        "test": "angle_unittests"
+      },
+      {
+        "args": [
+          "--enable-gpu",
+          "--test-launcher-bot-mode",
+          "--test-launcher-jobs=1",
+          "--gtest_filter=CastStreamingApiTestWithPixelOutput.EndToEnd*:TabCaptureApiPixelTest.EndToEnd*"
+        ],
+        "name": "tab_capture_end2end_tests",
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "gpu": "1002:6821",
+              "hidpi": "1",
+              "os": "Mac-10.13.6",
+              "pool": "Chrome-GPU"
+            }
+          ]
+        },
+        "test": "browser_tests"
+      },
+      {
+        "args": [
+          "--use-gpu-in-tests",
+          "--test-launcher-retry-limit=0"
+        ],
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "gpu": "1002:6821",
+              "hidpi": "1",
+              "os": "Mac-10.13.6",
+              "pool": "Chrome-GPU"
+            }
+          ],
+          "shards": 4
+        },
+        "test": "dawn_end2end_tests"
+      },
+      {
+        "args": [
+          "--use-gpu-in-tests",
+          "--use-cmd-decoder=validating"
+        ],
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "gpu": "1002:6821",
+              "hidpi": "1",
+              "os": "Mac-10.13.6",
+              "pool": "Chrome-GPU"
+            }
+          ]
+        },
+        "test": "gl_tests"
+      },
+      {
+        "args": [
+          "--use-gpu-in-tests"
+        ],
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "gpu": "1002:6821",
+              "hidpi": "1",
+              "os": "Mac-10.13.6",
+              "pool": "Chrome-GPU"
+            }
+          ]
+        },
+        "test": "gl_unittests"
+      },
+      {
+        "args": [
+          "--use-gpu-in-tests"
+        ],
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "gpu": "1002:6821",
+              "hidpi": "1",
+              "os": "Mac-10.13.6",
+              "pool": "Chrome-GPU"
+            }
+          ]
+        },
+        "test": "gles2_conform_test"
+      },
+      {
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "gpu": "1002:6821",
+              "hidpi": "1",
+              "os": "Mac-10.13.6",
+              "pool": "Chrome-GPU"
+            }
+          ]
+        },
+        "test": "gpu_unittests"
+      },
+      {
+        "args": [
+          "--gtest_filter=*Detection*",
+          "--use-gpu-in-tests"
+        ],
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "gpu": "1002:6821",
+              "hidpi": "1",
+              "os": "Mac-10.13.6",
+              "pool": "Chrome-GPU"
+            }
+          ]
+        },
+        "test": "services_unittests"
+      },
+      {
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "gpu": "1002:6821",
+              "hidpi": "1",
+              "os": "Mac-10.13.6",
+              "pool": "Chrome-GPU"
+            }
+          ]
+        },
+        "test": "swiftshader_unittests"
+      }
+    ],
+    "isolated_scripts": [
+      {
+        "args": [
+          "context_lost",
+          "--show-stdout",
+          "--browser=release",
+          "--passthrough",
+          "-v",
+          "--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc"
+        ],
+        "isolate_name": "telemetry_gpu_integration_test",
+        "name": "context_lost_tests",
+        "should_retry_with_patch": false,
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "gpu": "1002:6821",
+              "hidpi": "1",
+              "os": "Mac-10.13.6",
+              "pool": "Chrome-GPU"
+            }
+          ],
+          "idempotent": false
+        }
+      },
+      {
+        "args": [
+          "depth_capture",
+          "--show-stdout",
+          "--browser=release",
+          "--passthrough",
+          "-v",
+          "--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc"
+        ],
+        "isolate_name": "telemetry_gpu_integration_test",
+        "name": "depth_capture_tests",
+        "should_retry_with_patch": false,
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "gpu": "1002:6821",
+              "hidpi": "1",
+              "os": "Mac-10.13.6",
+              "pool": "Chrome-GPU"
+            }
+          ],
+          "idempotent": false
+        }
+      },
+      {
+        "args": [
+          "gpu_process",
+          "--show-stdout",
+          "--browser=release",
+          "--passthrough",
+          "-v",
+          "--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc"
+        ],
+        "isolate_name": "telemetry_gpu_integration_test",
+        "name": "gpu_process_launch_tests",
+        "should_retry_with_patch": false,
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "gpu": "1002:6821",
+              "hidpi": "1",
+              "os": "Mac-10.13.6",
+              "pool": "Chrome-GPU"
+            }
+          ],
+          "idempotent": false
+        }
+      },
+      {
+        "args": [
+          "hardware_accelerated_feature",
+          "--show-stdout",
+          "--browser=release",
+          "--passthrough",
+          "-v",
+          "--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc"
+        ],
+        "isolate_name": "telemetry_gpu_integration_test",
+        "name": "hardware_accelerated_feature_tests",
+        "should_retry_with_patch": false,
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "gpu": "1002:6821",
+              "hidpi": "1",
+              "os": "Mac-10.13.6",
+              "pool": "Chrome-GPU"
+            }
+          ],
+          "idempotent": false
+        }
+      },
+      {
+        "args": [
+          "info_collection",
+          "--show-stdout",
+          "--browser=release",
+          "--passthrough",
+          "-v",
+          "--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc",
+          "--expected-vendor-id",
+          "1002",
+          "--expected-device-id",
+          "6821"
+        ],
+        "isolate_name": "telemetry_gpu_integration_test",
+        "name": "info_collection_tests",
+        "should_retry_with_patch": false,
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "gpu": "1002:6821",
+              "hidpi": "1",
+              "os": "Mac-10.13.6",
+              "pool": "Chrome-GPU"
+            }
+          ],
+          "idempotent": false
+        }
+      },
+      {
+        "args": [
+          "maps",
+          "--show-stdout",
+          "--browser=release",
+          "--passthrough",
+          "-v",
+          "--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc",
+          "--dont-restore-color-profile-after-test",
+          "--os-type",
+          "mac",
+          "--build-revision",
+          "${got_revision}",
+          "--test-machine-name",
+          "${buildername}"
+        ],
+        "isolate_name": "telemetry_gpu_integration_test",
+        "name": "maps_pixel_test",
+        "should_retry_with_patch": false,
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "gpu": "1002:6821",
+              "hidpi": "1",
+              "os": "Mac-10.13.6",
+              "pool": "Chrome-GPU"
+            }
+          ],
+          "idempotent": false
+        }
+      },
+      {
+        "args": [
+          "pixel",
+          "--show-stdout",
+          "--browser=release",
+          "--passthrough",
+          "-v",
+          "--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc",
+          "--dont-restore-color-profile-after-test",
+          "--refimg-cloud-storage-bucket",
+          "chromium-gpu-archive/reference-images",
+          "--os-type",
+          "mac",
+          "--build-revision",
+          "${got_revision}",
+          "--test-machine-name",
+          "${buildername}"
+        ],
+        "isolate_name": "telemetry_gpu_integration_test",
+        "name": "pixel_test",
+        "non_precommit_args": [
+          "--upload-refimg-to-cloud-storage"
+        ],
+        "precommit_args": [
+          "--download-refimg-from-cloud-storage"
+        ],
+        "should_retry_with_patch": false,
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "gpu": "1002:6821",
+              "hidpi": "1",
+              "os": "Mac-10.13.6",
+              "pool": "Chrome-GPU"
+            }
+          ],
+          "idempotent": false
+        }
+      },
+      {
+        "args": [
+          "screenshot_sync",
+          "--show-stdout",
+          "--browser=release",
+          "--passthrough",
+          "-v",
+          "--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc",
+          "--dont-restore-color-profile-after-test"
+        ],
+        "isolate_name": "telemetry_gpu_integration_test",
+        "name": "screenshot_sync_tests",
+        "should_retry_with_patch": false,
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "gpu": "1002:6821",
+              "hidpi": "1",
+              "os": "Mac-10.13.6",
+              "pool": "Chrome-GPU"
+            }
+          ],
+          "idempotent": false
+        }
+      },
+      {
+        "args": [
+          "trace_test",
+          "--show-stdout",
+          "--browser=release",
+          "--passthrough",
+          "-v",
+          "--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc"
+        ],
+        "isolate_name": "telemetry_gpu_integration_test",
+        "name": "trace_test",
+        "should_retry_with_patch": false,
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "gpu": "1002:6821",
+              "hidpi": "1",
+              "os": "Mac-10.13.6",
+              "pool": "Chrome-GPU"
+            }
+          ],
+          "idempotent": false
+        }
+      },
+      {
+        "args": [
+          "webgl_conformance",
+          "--show-stdout",
+          "--browser=release",
+          "--passthrough",
+          "-v",
+          "--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc",
+          "--webgl-conformance-version=2.0.1",
+          "--read-abbreviated-json-results-from=../../content/test/data/gpu/webgl2_conformance_tests_output.json"
+        ],
+        "isolate_name": "telemetry_gpu_integration_test",
+        "name": "webgl2_conformance_tests",
+        "should_retry_with_patch": false,
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "gpu": "1002:6821",
+              "hidpi": "1",
+              "os": "Mac-10.13.6",
+              "pool": "Chrome-GPU"
+            }
+          ],
+          "idempotent": false,
+          "shards": 20
+        }
+      },
+      {
+        "args": [
+          "webgl_conformance",
+          "--show-stdout",
+          "--browser=release",
+          "--passthrough",
+          "-v",
+          "--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc"
+        ],
+        "isolate_name": "telemetry_gpu_integration_test",
+        "name": "webgl_conformance_tests",
+        "should_retry_with_patch": false,
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "gpu": "1002:6821",
+              "hidpi": "1",
+              "os": "Mac-10.13.6",
+              "pool": "Chrome-GPU"
+            }
+          ],
+          "idempotent": false,
+          "shards": 2
+        }
+      }
+    ]
+  },
+  "ANGLE GPU Mac Retina Release (NVIDIA)": {
+    "gtest_tests": [
+      {
+        "args": [
+          "--use-gpu-in-tests",
+          "--test-launcher-retry-limit=0"
+        ],
+        "should_retry_with_patch": false,
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "gpu": "10de:0fe9",
+              "hidpi": "1",
+              "os": "Mac-10.13.6",
+              "pool": "Chrome-GPU"
+            }
+          ],
+          "shards": 4
+        },
+        "test": "angle_end2end_tests"
+      },
+      {
+        "args": [
+          "--use-gpu-in-tests",
+          "--test-launcher-retry-limit=0"
+        ],
+        "should_retry_with_patch": false,
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "gpu": "10de:0fe9",
+              "hidpi": "1",
+              "os": "Mac-10.13.6",
+              "pool": "Chrome-GPU"
+            }
+          ]
+        },
+        "test": "angle_unittests"
+      },
+      {
+        "args": [
+          "--enable-gpu",
+          "--test-launcher-bot-mode",
+          "--test-launcher-jobs=1",
+          "--gtest_filter=CastStreamingApiTestWithPixelOutput.EndToEnd*:TabCaptureApiPixelTest.EndToEnd*"
+        ],
+        "name": "tab_capture_end2end_tests",
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "gpu": "10de:0fe9",
+              "hidpi": "1",
+              "os": "Mac-10.13.6",
+              "pool": "Chrome-GPU"
+            }
+          ]
+        },
+        "test": "browser_tests"
+      },
+      {
+        "args": [
+          "--use-gpu-in-tests",
+          "--test-launcher-retry-limit=0"
+        ],
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "gpu": "10de:0fe9",
+              "hidpi": "1",
+              "os": "Mac-10.13.6",
+              "pool": "Chrome-GPU"
+            }
+          ],
+          "shards": 4
+        },
+        "test": "dawn_end2end_tests"
+      },
+      {
+        "args": [
+          "--use-gpu-in-tests",
+          "--use-cmd-decoder=validating"
+        ],
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "gpu": "10de:0fe9",
+              "hidpi": "1",
+              "os": "Mac-10.13.6",
+              "pool": "Chrome-GPU"
+            }
+          ]
+        },
+        "test": "gl_tests"
+      },
+      {
+        "args": [
+          "--use-gpu-in-tests"
+        ],
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "gpu": "10de:0fe9",
+              "hidpi": "1",
+              "os": "Mac-10.13.6",
+              "pool": "Chrome-GPU"
+            }
+          ]
+        },
+        "test": "gl_unittests"
+      },
+      {
+        "args": [
+          "--use-gpu-in-tests"
+        ],
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "gpu": "10de:0fe9",
+              "hidpi": "1",
+              "os": "Mac-10.13.6",
+              "pool": "Chrome-GPU"
+            }
+          ]
+        },
+        "test": "gles2_conform_test"
+      },
+      {
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "gpu": "10de:0fe9",
+              "hidpi": "1",
+              "os": "Mac-10.13.6",
+              "pool": "Chrome-GPU"
+            }
+          ]
+        },
+        "test": "gpu_unittests"
+      },
+      {
+        "args": [
+          "--gtest_filter=*Detection*",
+          "--use-gpu-in-tests"
+        ],
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "gpu": "10de:0fe9",
+              "hidpi": "1",
+              "os": "Mac-10.13.6",
+              "pool": "Chrome-GPU"
+            }
+          ]
+        },
+        "test": "services_unittests"
+      },
+      {
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "gpu": "10de:0fe9",
+              "hidpi": "1",
+              "os": "Mac-10.13.6",
+              "pool": "Chrome-GPU"
+            }
+          ]
+        },
+        "test": "swiftshader_unittests"
+      }
+    ],
+    "isolated_scripts": [
+      {
+        "args": [
+          "context_lost",
+          "--show-stdout",
+          "--browser=release",
+          "--passthrough",
+          "-v",
+          "--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc"
+        ],
+        "isolate_name": "telemetry_gpu_integration_test",
+        "name": "context_lost_tests",
+        "should_retry_with_patch": false,
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "gpu": "10de:0fe9",
+              "hidpi": "1",
+              "os": "Mac-10.13.6",
+              "pool": "Chrome-GPU"
+            }
+          ],
+          "idempotent": false
+        }
+      },
+      {
+        "args": [
+          "depth_capture",
+          "--show-stdout",
+          "--browser=release",
+          "--passthrough",
+          "-v",
+          "--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc"
+        ],
+        "isolate_name": "telemetry_gpu_integration_test",
+        "name": "depth_capture_tests",
+        "should_retry_with_patch": false,
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "gpu": "10de:0fe9",
+              "hidpi": "1",
+              "os": "Mac-10.13.6",
+              "pool": "Chrome-GPU"
+            }
+          ],
+          "idempotent": false
+        }
+      },
+      {
+        "args": [
+          "gpu_process",
+          "--show-stdout",
+          "--browser=release",
+          "--passthrough",
+          "-v",
+          "--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc"
+        ],
+        "isolate_name": "telemetry_gpu_integration_test",
+        "name": "gpu_process_launch_tests",
+        "should_retry_with_patch": false,
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "gpu": "10de:0fe9",
+              "hidpi": "1",
+              "os": "Mac-10.13.6",
+              "pool": "Chrome-GPU"
+            }
+          ],
+          "idempotent": false
+        }
+      },
+      {
+        "args": [
+          "hardware_accelerated_feature",
+          "--show-stdout",
+          "--browser=release",
+          "--passthrough",
+          "-v",
+          "--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc"
+        ],
+        "isolate_name": "telemetry_gpu_integration_test",
+        "name": "hardware_accelerated_feature_tests",
+        "should_retry_with_patch": false,
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "gpu": "10de:0fe9",
+              "hidpi": "1",
+              "os": "Mac-10.13.6",
+              "pool": "Chrome-GPU"
+            }
+          ],
+          "idempotent": false
+        }
+      },
+      {
+        "args": [
+          "info_collection",
+          "--show-stdout",
+          "--browser=release",
+          "--passthrough",
+          "-v",
+          "--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc",
+          "--expected-vendor-id",
+          "10de",
+          "--expected-device-id",
+          "0fe9"
+        ],
+        "isolate_name": "telemetry_gpu_integration_test",
+        "name": "info_collection_tests",
+        "should_retry_with_patch": false,
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "gpu": "10de:0fe9",
+              "hidpi": "1",
+              "os": "Mac-10.13.6",
+              "pool": "Chrome-GPU"
+            }
+          ],
+          "idempotent": false
+        }
+      },
+      {
+        "args": [
+          "maps",
+          "--show-stdout",
+          "--browser=release",
+          "--passthrough",
+          "-v",
+          "--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc",
+          "--dont-restore-color-profile-after-test",
+          "--os-type",
+          "mac",
+          "--build-revision",
+          "${got_revision}",
+          "--test-machine-name",
+          "${buildername}"
+        ],
+        "isolate_name": "telemetry_gpu_integration_test",
+        "name": "maps_pixel_test",
+        "should_retry_with_patch": false,
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "gpu": "10de:0fe9",
+              "hidpi": "1",
+              "os": "Mac-10.13.6",
+              "pool": "Chrome-GPU"
+            }
+          ],
+          "idempotent": false
+        }
+      },
+      {
+        "args": [
+          "pixel",
+          "--show-stdout",
+          "--browser=release",
+          "--passthrough",
+          "-v",
+          "--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc",
+          "--dont-restore-color-profile-after-test",
+          "--refimg-cloud-storage-bucket",
+          "chromium-gpu-archive/reference-images",
+          "--os-type",
+          "mac",
+          "--build-revision",
+          "${got_revision}",
+          "--test-machine-name",
+          "${buildername}"
+        ],
+        "isolate_name": "telemetry_gpu_integration_test",
+        "name": "pixel_test",
+        "non_precommit_args": [
+          "--upload-refimg-to-cloud-storage"
+        ],
+        "precommit_args": [
+          "--download-refimg-from-cloud-storage"
+        ],
+        "should_retry_with_patch": false,
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "gpu": "10de:0fe9",
+              "hidpi": "1",
+              "os": "Mac-10.13.6",
+              "pool": "Chrome-GPU"
+            }
+          ],
+          "idempotent": false
+        }
+      },
+      {
+        "args": [
+          "screenshot_sync",
+          "--show-stdout",
+          "--browser=release",
+          "--passthrough",
+          "-v",
+          "--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc",
+          "--dont-restore-color-profile-after-test"
+        ],
+        "isolate_name": "telemetry_gpu_integration_test",
+        "name": "screenshot_sync_tests",
+        "should_retry_with_patch": false,
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "gpu": "10de:0fe9",
+              "hidpi": "1",
+              "os": "Mac-10.13.6",
+              "pool": "Chrome-GPU"
+            }
+          ],
+          "idempotent": false
+        }
+      },
+      {
+        "args": [
+          "trace_test",
+          "--show-stdout",
+          "--browser=release",
+          "--passthrough",
+          "-v",
+          "--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc"
+        ],
+        "isolate_name": "telemetry_gpu_integration_test",
+        "name": "trace_test",
+        "should_retry_with_patch": false,
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "gpu": "10de:0fe9",
+              "hidpi": "1",
+              "os": "Mac-10.13.6",
+              "pool": "Chrome-GPU"
+            }
+          ],
+          "idempotent": false
+        }
+      },
+      {
+        "args": [
+          "webgl_conformance",
+          "--show-stdout",
+          "--browser=release",
+          "--passthrough",
+          "-v",
+          "--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc",
+          "--webgl-conformance-version=2.0.1",
+          "--read-abbreviated-json-results-from=../../content/test/data/gpu/webgl2_conformance_tests_output.json"
+        ],
+        "isolate_name": "telemetry_gpu_integration_test",
+        "name": "webgl2_conformance_tests",
+        "should_retry_with_patch": false,
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "gpu": "10de:0fe9",
+              "hidpi": "1",
+              "os": "Mac-10.13.6",
+              "pool": "Chrome-GPU"
+            }
+          ],
+          "idempotent": false,
+          "shards": 20
+        }
+      },
+      {
+        "args": [
+          "webgl_conformance",
+          "--show-stdout",
+          "--browser=release",
+          "--passthrough",
+          "-v",
+          "--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc"
+        ],
+        "isolate_name": "telemetry_gpu_integration_test",
+        "name": "webgl_conformance_tests",
+        "should_retry_with_patch": false,
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "gpu": "10de:0fe9",
+              "hidpi": "1",
+              "os": "Mac-10.13.6",
+              "pool": "Chrome-GPU"
+            }
+          ],
+          "idempotent": false,
+          "shards": 2
+        }
+      }
+    ]
+  },
   "ANGLE GPU Win10 Release (Intel HD 630)": {
     "gtest_tests": [
       {
diff --git a/testing/buildbot/generate_buildbot_json.py b/testing/buildbot/generate_buildbot_json.py
index 3f8549bb..0167c8390 100755
--- a/testing/buildbot/generate_buildbot_json.py
+++ b/testing/buildbot/generate_buildbot_json.py
@@ -920,6 +920,11 @@
     # are defined only to be mirrored into trybots, and don't actually
     # exist on any of the waterfalls or consoles.
     return [
+      'ANGLE GPU Linux Release (Intel HD 630)',
+      'ANGLE GPU Linux Release (NVIDIA)',
+      'ANGLE GPU Mac Release (Intel)',
+      'ANGLE GPU Mac Retina Release (AMD)',
+      'ANGLE GPU Mac Retina Release (NVIDIA)',
       'ANGLE GPU Win10 Release (Intel HD 630)',
       'ANGLE GPU Win10 Release (NVIDIA)',
       'Dawn GPU Linux Release (Intel HD 630)',
diff --git a/testing/buildbot/waterfalls.pyl b/testing/buildbot/waterfalls.pyl
index 35e7987..ad46626 100644
--- a/testing/buildbot/waterfalls.pyl
+++ b/testing/buildbot/waterfalls.pyl
@@ -2070,6 +2070,67 @@
     'name': 'chromium.gpu.fyi',
     'machines': {
       # BEGIN Fake builder used as mirror targets for ANGLE's GPU tryservers
+      'ANGLE GPU Linux Release (Intel HD 630)': {
+        'os_type': 'linux',
+        'browser_config': 'release',
+        'mixins': [
+          'linux_intel_hd_630',
+        ],
+        'test_suites': {
+          # TODO(jmadill): Use custom test lists. crbug.com/822310
+          'gtest_tests': 'gpu_fyi_linux_release_gtests',
+          'gpu_telemetry_tests': 'gpu_fyi_linux_intel_and_nvidia_release_telemetry_tests',
+        },
+      },
+      'ANGLE GPU Linux Release (NVIDIA)': {
+        'os_type': 'linux',
+        'browser_config': 'release',
+        'mixins': [
+          'linux_nvidia_quadro_p400',
+        ],
+        'test_suites': {
+          # TODO(jmadill): Use custom test lists. crbug.com/822310
+          'gtest_tests': 'gpu_fyi_linux_release_gtests',
+          'isolated_scripts': 'gpu_angle_perftests',
+          'gpu_telemetry_tests': 'gpu_fyi_linux_intel_and_nvidia_release_telemetry_tests',
+        },
+      },
+      'ANGLE GPU Mac Release (Intel)': {
+        'os_type': 'mac',
+        'browser_config': 'release',
+        'mixins': [
+          'mac_mini_intel_gpu',
+        ],
+        'test_suites': {
+          # TODO(jmadill): Use custom test lists. crbug.com/822310
+          'gtest_tests': 'gpu_fyi_mac_release_gtests',
+          'gpu_telemetry_tests': 'gpu_fyi_mac_release_telemetry_tests',
+        },
+      },
+      'ANGLE GPU Mac Retina Release (AMD)': {
+        'os_type': 'mac',
+        'browser_config': 'release',
+        'mixins': [
+          'mac_retina_amd_gpu',
+        ],
+        'test_suites': {
+          # TODO(jmadill): Use custom test lists. crbug.com/822310
+          'gtest_tests': 'gpu_fyi_mac_release_gtests',
+          'gpu_telemetry_tests': 'gpu_fyi_mac_release_telemetry_tests',
+        },
+      },
+      'ANGLE GPU Mac Retina Release (NVIDIA)': {
+        'os_type': 'mac',
+        'browser_config': 'release',
+        'mixins': [
+          'mac_retina_nvidia_gpu',
+        ],
+        'test_suites': {
+          # TODO(jmadill): Use custom test lists. crbug.com/822310
+          'gtest_tests': 'gpu_fyi_mac_release_gtests',
+          'gpu_telemetry_tests': 'gpu_fyi_mac_release_telemetry_tests',
+        },
+      },
       'ANGLE GPU Win10 Release (Intel HD 630)': {
         'os_type': 'win',
         'browser_config': 'release',
diff --git a/testing/variations/fieldtrial_testing_config.json b/testing/variations/fieldtrial_testing_config.json
index f2cfeb1..6b34d2f 100644
--- a/testing/variations/fieldtrial_testing_config.json
+++ b/testing/variations/fieldtrial_testing_config.json
@@ -3014,9 +3014,9 @@
             ],
             "experiments": [
                 {
-                    "name": "Enabled_300_seconds_20190314",
+                    "name": "Enabled_60_seconds_20190408",
                     "params": {
-                        "unused_idle_socket_timeout_seconds": "300"
+                        "unused_idle_socket_timeout_seconds": "60"
                     },
                     "enable_features": [
                         "NetUnusedIdleSocketTimeout"
diff --git a/third_party/blink/common/mediastream/OWNERS b/third_party/blink/common/mediastream/OWNERS
index ed7be73..7f3f570 100644
--- a/third_party/blink/common/mediastream/OWNERS
+++ b/third_party/blink/common/mediastream/OWNERS
@@ -4,5 +4,5 @@
 per-file *_mojom_traits*.*=set noparent
 per-file *_mojom_traits*.*=file://ipc/SECURITY_OWNERS
 
-# TEAM: media-capture-and-streams@grotations.appspotmail.com
+# TEAM: webrtc-dev@chromium.org
 # COMPONENT: Blink>GetUserMedia
diff --git a/third_party/blink/public/common/mediastream/OWNERS b/third_party/blink/public/common/mediastream/OWNERS
index 155e1a8b..f247c7f 100644
--- a/third_party/blink/public/common/mediastream/OWNERS
+++ b/third_party/blink/public/common/mediastream/OWNERS
@@ -6,5 +6,5 @@
 per-file *.typemap=set noparent
 per-file *.typemap=file://ipc/SECURITY_OWNERS
 
-# TEAM: media-capture-and-streams@grotations.appspotmail.com
+# TEAM: webrtc-dev@chromium.org
 # COMPONENT: Blink>GetUserMedia
diff --git a/third_party/blink/public/mojom/mediastream/OWNERS b/third_party/blink/public/mojom/mediastream/OWNERS
index ff62c9c..1ec01e1 100644
--- a/third_party/blink/public/mojom/mediastream/OWNERS
+++ b/third_party/blink/public/mojom/mediastream/OWNERS
@@ -4,5 +4,5 @@
 per-file *.mojom=set noparent
 per-file *.mojom=file://ipc/SECURITY_OWNERS
 
-# TEAM: media-capture-and-streams@grotations.appspotmail.com
+# TEAM: webrtc-dev@chromium.org
 # COMPONENT: Blink>GetUserMedia
diff --git a/third_party/blink/public/platform/modules/mediastream/OWNERS b/third_party/blink/public/platform/modules/mediastream/OWNERS
index c205d4f9..32889cc 100644
--- a/third_party/blink/public/platform/modules/mediastream/OWNERS
+++ b/third_party/blink/public/platform/modules/mediastream/OWNERS
@@ -2,5 +2,5 @@
 
 per-file media_stream_audio_processor*=aluebs@chromium.org
 
-# TEAM: media-capture-and-streams@grotations.appspotmail.com
+# TEAM: webrtc-dev@chromium.org
 # COMPONENT: Blink>GetUserMedia
diff --git a/third_party/blink/public/web/modules/mediastream/OWNERS b/third_party/blink/public/web/modules/mediastream/OWNERS
index 4d93338..9d70184 100644
--- a/third_party/blink/public/web/modules/mediastream/OWNERS
+++ b/third_party/blink/public/web/modules/mediastream/OWNERS
@@ -1,4 +1,4 @@
 file://third_party/blink/common/mediastream/OWNERS
 
-# TEAM: media-capture-and-streams@grotations.appspotmail.com
+# TEAM: webrtc-dev@chromium.org
 # COMPONENT: Blink>GetUserMedia
diff --git a/third_party/blink/public/web/web_ax_object.h b/third_party/blink/public/web/web_ax_object.h
index c6d5dc9c..9e401922 100644
--- a/third_party/blink/public/web/web_ax_object.h
+++ b/third_party/blink/public/web/web_ax_object.h
@@ -272,7 +272,6 @@
   BLINK_EXPORT bool HasComputedStyle() const;
   BLINK_EXPORT WebString ComputedStyleDisplay() const;
   BLINK_EXPORT bool AccessibilityIsIgnored() const;
-  BLINK_EXPORT bool LineBreaks(WebVector<int>&) const;
   BLINK_EXPORT void Markers(WebVector<ax::mojom::MarkerType>& types,
                             WebVector<int>& starts,
                             WebVector<int>& ends) const;
diff --git a/third_party/blink/public/web/web_frame.h b/third_party/blink/public/web/web_frame.h
index 188e207..e5dd3db 100644
--- a/third_party/blink/public/web/web_frame.h
+++ b/third_party/blink/public/web/web_frame.h
@@ -33,7 +33,6 @@
 
 #include <memory>
 #include "cc/paint/paint_canvas.h"
-#include "third_party/blink/public/common/feature_policy/feature_policy.h"
 #include "third_party/blink/public/platform/web_common.h"
 #include "third_party/blink/public/platform/web_insecure_request_policy.h"
 #include "third_party/blink/public/web/web_frame_load_type.h"
@@ -52,6 +51,7 @@
 class WebSecurityOrigin;
 class WebView;
 enum class WebSandboxFlags;
+struct FramePolicy;
 struct WebFrameOwnerProperties;
 struct WebRect;
 
@@ -104,7 +104,7 @@
   // parent is in another process and it dynamically updates this frame's
   // sandbox flags or container policy. The new policy won't take effect until
   // the next navigation.
-  void SetFrameOwnerPolicy(WebSandboxFlags, const blink::ParsedFeaturePolicy&);
+  void SetFrameOwnerPolicy(const FramePolicy&);
 
   // The frame's insecure request policy.
   WebInsecureRequestPolicy GetInsecureRequestPolicy() const;
diff --git a/third_party/blink/public/web/web_local_frame.h b/third_party/blink/public/web/web_local_frame.h
index e1de94d8..dd216c89 100644
--- a/third_party/blink/public/web/web_local_frame.h
+++ b/third_party/blink/public/web/web_local_frame.h
@@ -112,8 +112,7 @@
       blink::InterfaceRegistry*,
       mojo::ScopedMessagePipeHandle,
       WebFrame* previous_web_frame,
-      WebSandboxFlags,
-      ParsedFeaturePolicy);
+      const FramePolicy&);
 
   // Creates a new local child of this frame. Similar to the other methods that
   // create frames, the returned frame should be freed by calling Close() when
diff --git a/third_party/blink/public/web/web_local_frame_client.h b/third_party/blink/public/web/web_local_frame_client.h
index 8ec78aa..3d3d4543 100644
--- a/third_party/blink/public/web/web_local_frame_client.h
+++ b/third_party/blink/public/web/web_local_frame_client.h
@@ -115,6 +115,7 @@
 class WebURL;
 class WebURLResponse;
 class WebUserMediaClient;
+struct FramePolicy;
 struct WebConsoleMessage;
 struct WebContextMenuData;
 struct WebFullscreenOptions;
@@ -225,15 +226,13 @@
   // to prevent the new child frame from being attached. Otherwise, embedders
   // should create a new WebLocalFrame, insert it into the frame tree, and
   // return the created frame.
-  virtual WebLocalFrame* CreateChildFrame(
-      WebLocalFrame* parent,
-      WebTreeScopeType,
-      const WebString& name,
-      const WebString& fallback_name,
-      WebSandboxFlags sandbox_flags,
-      const ParsedFeaturePolicy& container_policy,
-      const WebFrameOwnerProperties&,
-      FrameOwnerElementType) {
+  virtual WebLocalFrame* CreateChildFrame(WebLocalFrame* parent,
+                                          WebTreeScopeType,
+                                          const WebString& name,
+                                          const WebString& fallback_name,
+                                          const FramePolicy&,
+                                          const WebFrameOwnerProperties&,
+                                          FrameOwnerElementType) {
     return nullptr;
   }
 
@@ -283,10 +282,8 @@
 
   // The sandbox flags or container policy have changed for a child frame of
   // this frame.
-  virtual void DidChangeFramePolicy(
-      WebFrame* child_frame,
-      WebSandboxFlags flags,
-      const ParsedFeaturePolicy& container_policy) {}
+  virtual void DidChangeFramePolicy(WebFrame* child_frame, const FramePolicy&) {
+  }
 
   // Called when a Feature-Policy or Content-Security-Policy HTTP header (for
   // sandbox flags) is encountered while loading the frame's document.
diff --git a/third_party/blink/public/web/web_remote_frame.h b/third_party/blink/public/web/web_remote_frame.h
index 6a6a04c..b140fc2 100644
--- a/third_party/blink/public/web/web_remote_frame.h
+++ b/third_party/blink/public/web/web_remote_frame.h
@@ -52,20 +52,18 @@
   // beginning.
   virtual WebLocalFrame* CreateLocalChild(WebTreeScopeType,
                                           const WebString& name,
-                                          WebSandboxFlags,
+                                          const FramePolicy&,
                                           WebLocalFrameClient*,
                                           blink::InterfaceRegistry*,
                                           mojo::ScopedMessagePipeHandle,
                                           WebFrame* previous_sibling,
-                                          const ParsedFeaturePolicy&,
                                           const WebFrameOwnerProperties&,
                                           FrameOwnerElementType,
                                           WebFrame* opener) = 0;
 
   virtual WebRemoteFrame* CreateRemoteChild(WebTreeScopeType,
                                             const WebString& name,
-                                            WebSandboxFlags,
-                                            const ParsedFeaturePolicy&,
+                                            const FramePolicy&,
                                             FrameOwnerElementType,
                                             WebRemoteFrameClient*,
                                             WebFrame* opener) = 0;
diff --git a/third_party/blink/renderer/bindings/core/v8/local_window_proxy.cc b/third_party/blink/renderer/bindings/core/v8/local_window_proxy.cc
index a56856c..68d19bf 100644
--- a/third_party/blink/renderer/bindings/core/v8/local_window_proxy.cc
+++ b/third_party/blink/renderer/bindings/core/v8/local_window_proxy.cc
@@ -57,6 +57,7 @@
 #include "third_party/blink/renderer/platform/bindings/v8_dom_wrapper.h"
 #include "third_party/blink/renderer/platform/bindings/v8_private_property.h"
 #include "third_party/blink/renderer/platform/heap/handle.h"
+#include "third_party/blink/renderer/platform/heap/heap.h"
 #include "third_party/blink/renderer/platform/histogram.h"
 #include "third_party/blink/renderer/platform/instrumentation/tracing/trace_event.h"
 #include "third_party/blink/renderer/platform/weborigin/security_origin.h"
@@ -260,7 +261,7 @@
   DidAttachGlobalObject();
 #endif
 
-  script_state_ = ScriptState::Create(context, world_);
+  script_state_ = MakeGarbageCollected<ScriptState>(context, world_);
 
   DCHECK(lifecycle_ == Lifecycle::kContextIsUninitialized ||
          lifecycle_ == Lifecycle::kGlobalObjectIsDetached);
diff --git a/third_party/blink/renderer/bindings/core/v8/scheduled_action.cc b/third_party/blink/renderer/bindings/core/v8/scheduled_action.cc
index 4a92bd3..1e0faac 100644
--- a/third_party/blink/renderer/bindings/core/v8/scheduled_action.cc
+++ b/third_party/blink/renderer/bindings/core/v8/scheduled_action.cc
@@ -45,6 +45,7 @@
 #include "third_party/blink/renderer/core/workers/worker_global_scope.h"
 #include "third_party/blink/renderer/core/workers/worker_thread.h"
 #include "third_party/blink/renderer/platform/bindings/script_state.h"
+#include "third_party/blink/renderer/platform/heap/heap.h"
 #include "third_party/blink/renderer/platform/instrumentation/tracing/trace_event.h"
 #include "third_party/blink/renderer/platform/wtf/casting.h"
 
@@ -85,16 +86,19 @@
 ScheduledAction::ScheduledAction(ScriptState* script_state,
                                  V8Function* function,
                                  const Vector<ScriptValue>& arguments)
-    : script_state_(ScriptStateProtectingContext::Create(script_state)),
+    : script_state_(
+          MakeGarbageCollected<ScriptStateProtectingContext>(script_state)),
       function_(function),
       arguments_(arguments) {}
 
 ScheduledAction::ScheduledAction(ScriptState* script_state, const String& code)
-    : script_state_(ScriptStateProtectingContext::Create(script_state)),
+    : script_state_(
+          MakeGarbageCollected<ScriptStateProtectingContext>(script_state)),
       code_(code) {}
 
 ScheduledAction::ScheduledAction(ScriptState* script_state)
-    : script_state_(ScriptStateProtectingContext::Create(script_state)) {}
+    : script_state_(
+          MakeGarbageCollected<ScriptStateProtectingContext>(script_state)) {}
 
 ScheduledAction::~ScheduledAction() {
   // Verify that owning DOMTimer has eagerly disposed.
diff --git a/third_party/blink/renderer/bindings/core/v8/script_promise_property_test.cc b/third_party/blink/renderer/bindings/core/v8/script_promise_property_test.cc
index f44f349..251c4549 100644
--- a/third_party/blink/renderer/bindings/core/v8/script_promise_property_test.cc
+++ b/third_party/blink/renderer/bindings/core/v8/script_promise_property_test.cc
@@ -21,6 +21,7 @@
 #include "third_party/blink/renderer/platform/bindings/dom_wrapper_world.h"
 #include "third_party/blink/renderer/platform/bindings/script_state.h"
 #include "third_party/blink/renderer/platform/heap/handle.h"
+#include "third_party/blink/renderer/platform/heap/heap.h"
 #include "v8/include/v8.h"
 
 namespace blink {
@@ -104,7 +105,7 @@
   ScriptPromisePropertyTestBase()
       : page_(std::make_unique<DummyPageHolder>(IntSize(1, 1))) {
     v8::HandleScope handle_scope(GetIsolate());
-    other_script_state_ = ScriptState::Create(
+    other_script_state_ = MakeGarbageCollected<ScriptState>(
         v8::Context::New(GetIsolate()),
         DOMWrapperWorld::EnsureIsolatedWorld(GetIsolate(), 1));
   }
diff --git a/third_party/blink/renderer/bindings/core/v8/worker_or_worklet_script_controller.cc b/third_party/blink/renderer/bindings/core/v8/worker_or_worklet_script_controller.cc
index 611ea66..cfa7e4e 100644
--- a/third_party/blink/renderer/bindings/core/v8/worker_or_worklet_script_controller.cc
+++ b/third_party/blink/renderer/bindings/core/v8/worker_or_worklet_script_controller.cc
@@ -53,6 +53,7 @@
 #include "third_party/blink/renderer/platform/bindings/v8_dom_wrapper.h"
 #include "third_party/blink/renderer/platform/bindings/v8_object_constructor.h"
 #include "third_party/blink/renderer/platform/bindings/wrapper_type_info.h"
+#include "third_party/blink/renderer/platform/heap/heap.h"
 #include "third_party/blink/renderer/platform/heap/thread_state.h"
 #include "v8/include/v8.h"
 
@@ -163,7 +164,7 @@
   if (context.IsEmpty())
     return false;
 
-  script_state_ = ScriptState::Create(context, world_);
+  script_state_ = MakeGarbageCollected<ScriptState>(context, world_);
 
   ScriptState::Scope scope(script_state_);
 
diff --git a/third_party/blink/renderer/core/dom/document.cc b/third_party/blink/renderer/core/dom/document.cc
index b561dfd..a899165 100644
--- a/third_party/blink/renderer/core/dom/document.cc
+++ b/third_party/blink/renderer/core/dom/document.cc
@@ -6216,7 +6216,7 @@
   // If this frame is not the main frame, then get the container policy from its
   // owner.
   if (frame_ && frame_->Owner())
-    return frame_->Owner()->ContainerPolicy();
+    return frame_->Owner()->GetFramePolicy().container_policy;
   return ParsedFeaturePolicy();
 }
 
diff --git a/third_party/blink/renderer/core/exported/local_frame_client_impl.cc b/third_party/blink/renderer/core/exported/local_frame_client_impl.cc
index 3493baa..7975a1b 100644
--- a/third_party/blink/renderer/core/exported/local_frame_client_impl.cc
+++ b/third_party/blink/renderer/core/exported/local_frame_client_impl.cc
@@ -931,13 +931,11 @@
 
 void LocalFrameClientImpl::DidChangeFramePolicy(
     Frame* child_frame,
-    SandboxFlags flags,
-    const ParsedFeaturePolicy& container_policy) {
+    const FramePolicy& frame_policy) {
   if (!web_frame_->Client())
     return;
-  web_frame_->Client()->DidChangeFramePolicy(
-      WebFrame::FromFrame(child_frame), static_cast<WebSandboxFlags>(flags),
-      container_policy);
+  web_frame_->Client()->DidChangeFramePolicy(WebFrame::FromFrame(child_frame),
+                                             frame_policy);
 }
 
 void LocalFrameClientImpl::DidSetFramePolicyHeaders(
diff --git a/third_party/blink/renderer/core/exported/local_frame_client_impl.h b/third_party/blink/renderer/core/exported/local_frame_client_impl.h
index 0419fbac..3dc160b 100644
--- a/third_party/blink/renderer/core/exported/local_frame_client_impl.h
+++ b/third_party/blink/renderer/core/exported/local_frame_client_impl.h
@@ -206,9 +206,7 @@
   void DidChangeName(const String&) override;
   void DidEnforceInsecureRequestPolicy(WebInsecureRequestPolicy) override;
   void DidEnforceInsecureNavigationsSet(const std::vector<unsigned>&) override;
-  void DidChangeFramePolicy(Frame* child_frame,
-                            SandboxFlags,
-                            const ParsedFeaturePolicy&) override;
+  void DidChangeFramePolicy(Frame* child_frame, const FramePolicy&) override;
   void DidSetFramePolicyHeaders(
       SandboxFlags,
       const ParsedFeaturePolicy& parsed_header) override;
diff --git a/third_party/blink/renderer/core/exported/web_frame.cc b/third_party/blink/renderer/core/exported/web_frame.cc
index 321368a..5e13338 100644
--- a/third_party/blink/renderer/core/exported/web_frame.cc
+++ b/third_party/blink/renderer/core/exported/web_frame.cc
@@ -159,14 +159,11 @@
       ToCoreFrame(*this)->GetSecurityContext()->GetSecurityOrigin());
 }
 
-void WebFrame::SetFrameOwnerPolicy(
-    WebSandboxFlags flags,
-    const blink::ParsedFeaturePolicy& container_policy) {
+void WebFrame::SetFrameOwnerPolicy(const FramePolicy& frame_policy) {
   // At the moment, this is only used to replicate sandbox flags and container
   // policy for frames with a remote owner.
-  auto* owner = To<RemoteFrameOwner>(ToCoreFrame(*this)->Owner());
-  owner->SetSandboxFlags(static_cast<SandboxFlags>(flags));
-  owner->SetContainerPolicy(container_policy);
+  To<RemoteFrameOwner>(ToCoreFrame(*this)->Owner())
+      ->SetFramePolicy(frame_policy);
 }
 
 WebInsecureRequestPolicy WebFrame::GetInsecureRequestPolicy() const {
diff --git a/third_party/blink/renderer/core/exported/web_frame_test.cc b/third_party/blink/renderer/core/exported/web_frame_test.cc
index a8fba24..da0a3200 100644
--- a/third_party/blink/renderer/core/exported/web_frame_test.cc
+++ b/third_party/blink/renderer/core/exported/web_frame_test.cc
@@ -4712,8 +4712,7 @@
                                   WebTreeScopeType scope,
                                   const WebString& name,
                                   const WebString& fallback_name,
-                                  WebSandboxFlags sandbox_flags,
-                                  const ParsedFeaturePolicy& container_policy,
+                                  const FramePolicy&,
                                   const WebFrameOwnerProperties&,
                                   FrameOwnerElementType) override {
     return CreateLocalChild(*parent, scope,
@@ -7453,8 +7452,7 @@
       WebTreeScopeType scope,
       const WebString&,
       const WebString&,
-      WebSandboxFlags,
-      const ParsedFeaturePolicy&,
+      const FramePolicy&,
       const WebFrameOwnerProperties& frame_owner_properties,
       FrameOwnerElementType) override {
     auto child = std::make_unique<TestCachePolicyWebFrameClient>();
@@ -7775,8 +7773,7 @@
                                   WebTreeScopeType scope,
                                   const WebString& name,
                                   const WebString& fallback_name,
-                                  WebSandboxFlags,
-                                  const ParsedFeaturePolicy&,
+                                  const FramePolicy&,
                                   const WebFrameOwnerProperties&,
                                   FrameOwnerElementType) override {
     return CreateLocalChild(*parent, scope, &child_client_);
@@ -7917,8 +7914,7 @@
       WebTreeScopeType scope,
       const WebString& name,
       const WebString& fallback_name,
-      WebSandboxFlags sandbox_flags,
-      const ParsedFeaturePolicy& container_policy,
+      const FramePolicy&,
       const WebFrameOwnerProperties& frame_owner_properties,
       FrameOwnerElementType) override {
     ++call_count_;
@@ -10806,8 +10802,7 @@
                                   WebTreeScopeType scope,
                                   const WebString& name,
                                   const WebString& fallback_name,
-                                  WebSandboxFlags,
-                                  const ParsedFeaturePolicy&,
+                                  const FramePolicy&,
                                   const WebFrameOwnerProperties&,
                                   FrameOwnerElementType) override {
     return CreateLocalChild(*parent, scope, &child_client_);
@@ -12324,8 +12319,7 @@
                                     WebTreeScopeType scope,
                                     const WebString& name,
                                     const WebString& fallback_name,
-                                    WebSandboxFlags sandbox_flags,
-                                    const ParsedFeaturePolicy& container_policy,
+                                    const FramePolicy&,
                                     const WebFrameOwnerProperties&,
                                     FrameOwnerElementType) override {
       return CreateLocalChild(*parent, scope, &child_client_);
@@ -12589,8 +12583,7 @@
       WebTreeScopeType scope,
       const WebString&,
       const WebString&,
-      WebSandboxFlags,
-      const ParsedFeaturePolicy& container_policy,
+      const FramePolicy&,
       const WebFrameOwnerProperties& frameOwnerProperties,
       FrameOwnerElementType) override {
     DCHECK(child_client_);
diff --git a/third_party/blink/renderer/core/exported/web_plugin_container_impl.cc b/third_party/blink/renderer/core/exported/web_plugin_container_impl.cc
index 143562e2..2dd6a1c9 100644
--- a/third_party/blink/renderer/core/exported/web_plugin_container_impl.cc
+++ b/third_party/blink/renderer/core/exported/web_plugin_container_impl.cc
@@ -178,6 +178,7 @@
         gfx::Vector2dF(frame_rect_.X(), frame_rect_.Y()));
     layer_->SetBounds(gfx::Size(frame_rect_.Size()));
     layer_->SetIsDrawable(true);
+    layer_->SetHitTestable(true);
     // When compositing is after paint, composited plugins should have their
     // layers inserted rather than invoking WebPlugin::paint.
     RecordForeignLayer(context, DisplayItem::kForeignLayerPlugin, layer_);
diff --git a/third_party/blink/renderer/core/exported/web_plugin_container_test.cc b/third_party/blink/renderer/core/exported/web_plugin_container_test.cc
index ae580f0..425c1b7 100644
--- a/third_party/blink/renderer/core/exported/web_plugin_container_test.cc
+++ b/third_party/blink/renderer/core/exported/web_plugin_container_test.cc
@@ -203,8 +203,7 @@
                                   WebTreeScopeType scope,
                                   const WebString& name,
                                   const WebString& fallback_name,
-                                  WebSandboxFlags sandbox_flags,
-                                  const ParsedFeaturePolicy& container_policy,
+                                  const FramePolicy&,
                                   const WebFrameOwnerProperties&,
                                   FrameOwnerElementType owner_type) override {
     return CreateLocalChild(*parent, scope,
diff --git a/third_party/blink/renderer/core/exported/web_remote_frame_impl.cc b/third_party/blink/renderer/core/exported/web_remote_frame_impl.cc
index d16ac271..4b57f17 100644
--- a/third_party/blink/renderer/core/exported/web_remote_frame_impl.cc
+++ b/third_party/blink/renderer/core/exported/web_remote_frame_impl.cc
@@ -141,12 +141,11 @@
 WebLocalFrame* WebRemoteFrameImpl::CreateLocalChild(
     WebTreeScopeType scope,
     const WebString& name,
-    WebSandboxFlags sandbox_flags,
+    const FramePolicy& frame_policy,
     WebLocalFrameClient* client,
     blink::InterfaceRegistry* interface_registry,
     mojo::ScopedMessagePipeHandle document_interface_broker_handle,
     WebFrame* previous_sibling,
-    const ParsedFeaturePolicy& container_policy,
     const WebFrameOwnerProperties& frame_owner_properties,
     FrameOwnerElementType frame_owner_element_type,
     WebFrame* opener) {
@@ -155,8 +154,7 @@
       std::move(document_interface_broker_handle), opener);
   InsertAfter(child, previous_sibling);
   auto* owner = MakeGarbageCollected<RemoteFrameOwner>(
-      static_cast<SandboxFlags>(sandbox_flags), container_policy,
-      frame_owner_properties, frame_owner_element_type);
+      frame_policy, frame_owner_properties, frame_owner_element_type);
   child->InitializeCoreFrame(*GetFrame()->GetPage(), owner, name);
   DCHECK(child->GetFrame());
   return child;
@@ -173,8 +171,7 @@
 WebRemoteFrame* WebRemoteFrameImpl::CreateRemoteChild(
     WebTreeScopeType scope,
     const WebString& name,
-    WebSandboxFlags sandbox_flags,
-    const ParsedFeaturePolicy& container_policy,
+    const FramePolicy& frame_policy,
     FrameOwnerElementType frame_owner_element_type,
     WebRemoteFrameClient* client,
     WebFrame* opener) {
@@ -182,8 +179,7 @@
   child->SetOpener(opener);
   AppendChild(child);
   auto* owner = MakeGarbageCollected<RemoteFrameOwner>(
-      static_cast<SandboxFlags>(sandbox_flags), container_policy,
-      WebFrameOwnerProperties(), frame_owner_element_type);
+      frame_policy, WebFrameOwnerProperties(), frame_owner_element_type);
   child->InitializeCoreFrame(*GetFrame()->GetPage(), owner, name);
   return child;
 }
@@ -266,7 +262,7 @@
   }
   ParsedFeaturePolicy container_policy;
   if (GetFrame()->Owner())
-    container_policy = GetFrame()->Owner()->ContainerPolicy();
+    container_policy = GetFrame()->Owner()->GetFramePolicy().container_policy;
   const FeaturePolicy::FeatureState& opener_feature_state =
       frame_->OpenerFeatureState();
   GetFrame()->GetSecurityContext()->InitializeFeaturePolicy(
diff --git a/third_party/blink/renderer/core/exported/web_remote_frame_impl.h b/third_party/blink/renderer/core/exported/web_remote_frame_impl.h
index 9a469a3e0..694ea71 100644
--- a/third_party/blink/renderer/core/exported/web_remote_frame_impl.h
+++ b/third_party/blink/renderer/core/exported/web_remote_frame_impl.h
@@ -47,19 +47,17 @@
   // WebRemoteFrame methods:
   WebLocalFrame* CreateLocalChild(WebTreeScopeType,
                                   const WebString& name,
-                                  WebSandboxFlags,
+                                  const FramePolicy&,
                                   WebLocalFrameClient*,
                                   blink::InterfaceRegistry*,
                                   mojo::ScopedMessagePipeHandle,
                                   WebFrame* previous_sibling,
-                                  const ParsedFeaturePolicy&,
                                   const WebFrameOwnerProperties&,
                                   FrameOwnerElementType,
                                   WebFrame* opener) override;
   WebRemoteFrame* CreateRemoteChild(WebTreeScopeType,
                                     const WebString& name,
-                                    WebSandboxFlags,
-                                    const ParsedFeaturePolicy&,
+                                    const FramePolicy&,
                                     FrameOwnerElementType,
                                     WebRemoteFrameClient*,
                                     WebFrame* opener) override;
diff --git a/third_party/blink/renderer/core/exported/web_view_test.cc b/third_party/blink/renderer/core/exported/web_view_test.cc
index 48ca3b4..68df7b3 100644
--- a/third_party/blink/renderer/core/exported/web_view_test.cc
+++ b/third_party/blink/renderer/core/exported/web_view_test.cc
@@ -3692,8 +3692,7 @@
                                   WebTreeScopeType,
                                   const WebString& name,
                                   const WebString& fallback_name,
-                                  WebSandboxFlags,
-                                  const ParsedFeaturePolicy&,
+                                  const FramePolicy&,
                                   const WebFrameOwnerProperties&,
                                   FrameOwnerElementType) override;
 
@@ -3708,14 +3707,13 @@
     WebTreeScopeType scope,
     const WebString& name,
     const WebString& fallback_name,
-    WebSandboxFlags sandbox_flags,
-    const ParsedFeaturePolicy& container_policy,
+    const FramePolicy& frame_policy,
     const WebFrameOwnerProperties& frame_owner_properties,
     FrameOwnerElementType frame_owner_element_type) {
   ++count_;
   return TestWebFrameClient::CreateChildFrame(
-      parent, scope, name, fallback_name, sandbox_flags, container_policy,
-      frame_owner_properties, frame_owner_element_type);
+      parent, scope, name, fallback_name, frame_policy, frame_owner_properties,
+      frame_owner_element_type);
 }
 
 TEST_F(WebViewTest, ChangeDisplayMode) {
diff --git a/third_party/blink/renderer/core/frame/frame_overlay.cc b/third_party/blink/renderer/core/frame/frame_overlay.cc
index 68080a59..a4c344f7 100644
--- a/third_party/blink/renderer/core/frame/frame_overlay.cc
+++ b/third_party/blink/renderer/core/frame/frame_overlay.cc
@@ -71,6 +71,7 @@
   if (!layer_) {
     layer_ = std::make_unique<GraphicsLayer>(*this);
     layer_->SetDrawsContent(true);
+    layer_->SetHitTestable(true);
 
     if (!RuntimeEnabledFeatures::BlinkGenPropertyTreesEnabled()) {
       // This is required for contents of overlay to stay in sync with the page
diff --git a/third_party/blink/renderer/core/frame/frame_owner.h b/third_party/blink/renderer/core/frame/frame_owner.h
index c31b88e..bd65ef0 100644
--- a/third_party/blink/renderer/core/frame/frame_owner.h
+++ b/third_party/blink/renderer/core/frame/frame_owner.h
@@ -5,11 +5,11 @@
 #ifndef THIRD_PARTY_BLINK_RENDERER_CORE_FRAME_FRAME_OWNER_H_
 #define THIRD_PARTY_BLINK_RENDERER_CORE_FRAME_FRAME_OWNER_H_
 
-#include "third_party/blink/public/common/feature_policy/feature_policy.h"
+#include "third_party/blink/public/common/frame/frame_policy.h"
 #include "third_party/blink/renderer/core/core_export.h"
-#include "third_party/blink/renderer/core/frame/sandbox_flags.h"
 #include "third_party/blink/renderer/core/scroll/scroll_types.h"
 #include "third_party/blink/renderer/platform/heap/handle.h"
+#include "third_party/blink/renderer/platform/wtf/text/atomic_string.h"
 
 namespace blink {
 
@@ -32,8 +32,7 @@
   virtual Frame* ContentFrame() const = 0;
   virtual void SetContentFrame(Frame&) = 0;
   virtual void ClearContentFrame() = 0;
-
-  virtual SandboxFlags GetSandboxFlags() const = 0;
+  virtual const FramePolicy& GetFramePolicy() const = 0;
   // Note: there is a subtle ordering dependency here: if a page load needs to
   // report resource timing information, it *must* do so before calling
   // DispatchLoad().
@@ -71,7 +70,6 @@
   virtual bool AllowPaymentRequest() const = 0;
   virtual bool IsDisplayNone() const = 0;
   virtual AtomicString RequiredCsp() const = 0;
-  virtual const ParsedFeaturePolicy& ContainerPolicy() const = 0;
 
   // Returns whether or not children of the owned frame should be lazily loaded.
   virtual bool ShouldLazyLoadChildren() const = 0;
@@ -92,8 +90,9 @@
   Frame* ContentFrame() const override { return nullptr; }
   void SetContentFrame(Frame&) override {}
   void ClearContentFrame() override {}
-  SandboxFlags GetSandboxFlags() const override {
-    return WebSandboxFlags::kNone;
+  const FramePolicy& GetFramePolicy() const override {
+    DEFINE_STATIC_LOCAL(FramePolicy, frame_policy, ());
+    return frame_policy;
   }
   void AddResourceTiming(const ResourceTimingInfo&) override {}
   void DispatchLoad() override {}
@@ -111,10 +110,6 @@
   bool AllowPaymentRequest() const override { return false; }
   bool IsDisplayNone() const override { return false; }
   AtomicString RequiredCsp() const override { return g_null_atom; }
-  const ParsedFeaturePolicy& ContainerPolicy() const override {
-    DEFINE_STATIC_LOCAL(ParsedFeaturePolicy, container_policy, ());
-    return container_policy;
-  }
   bool ShouldLazyLoadChildren() const override { return false; }
 
  private:
diff --git a/third_party/blink/renderer/core/frame/frame_test_helpers.cc b/third_party/blink/renderer/core/frame/frame_test_helpers.cc
index e8866c55..2d07afa8 100644
--- a/third_party/blink/renderer/core/frame/frame_test_helpers.cc
+++ b/third_party/blink/renderer/core/frame/frame_test_helpers.cc
@@ -36,6 +36,7 @@
 #include "cc/test/test_ukm_recorder_factory.h"
 #include "cc/trees/layer_tree_host.h"
 #include "cc/trees/layer_tree_settings.h"
+#include "third_party/blink/public/common/frame/frame_policy.h"
 #include "third_party/blink/public/platform/platform.h"
 #include "third_party/blink/public/platform/web_data.h"
 #include "third_party/blink/public/platform/web_string.h"
@@ -227,7 +228,7 @@
   auto* frame = To<WebLocalFrameImpl>(WebLocalFrame::CreateProvisional(
       client, nullptr,
       mojo::MakeRequest(&document_interface_broker).PassMessagePipe(),
-      &old_frame, WebSandboxFlags::kNone, ParsedFeaturePolicy()));
+      &old_frame, FramePolicy()));
   client->Bind(frame, std::move(owned_client));
   std::unique_ptr<TestWebWidgetClient> widget_client;
   // Create a local root, if necessary.
@@ -269,10 +270,9 @@
   client = CreateDefaultClientIfNeeded(client, owned_client);
   mojom::blink::DocumentInterfaceBrokerPtrInfo document_interface_broker;
   auto* frame = To<WebLocalFrameImpl>(parent.CreateLocalChild(
-      WebTreeScopeType::kDocument, name, WebSandboxFlags::kNone, client,
-      nullptr, mojo::MakeRequest(&document_interface_broker).PassMessagePipe(),
-      previous_sibling, ParsedFeaturePolicy(), properties,
-      FrameOwnerElementType::kIframe, nullptr));
+      WebTreeScopeType::kDocument, name, FramePolicy(), client, nullptr,
+      mojo::MakeRequest(&document_interface_broker).PassMessagePipe(),
+      previous_sibling, properties, FrameOwnerElementType::kIframe, nullptr));
   client->Bind(frame, std::move(owned_client));
 
   std::unique_ptr<TestWebWidgetClient> owned_widget_client;
@@ -299,8 +299,8 @@
   std::unique_ptr<TestWebRemoteFrameClient> owned_client;
   client = CreateDefaultClientIfNeeded(client, owned_client);
   auto* frame = ToWebRemoteFrameImpl(parent.CreateRemoteChild(
-      WebTreeScopeType::kDocument, name, WebSandboxFlags::kNone,
-      ParsedFeaturePolicy(), FrameOwnerElementType::kIframe, client, nullptr));
+      WebTreeScopeType::kDocument, name, FramePolicy(),
+      FrameOwnerElementType::kIframe, client, nullptr));
   client->Bind(frame, std::move(owned_client));
   if (!security_origin)
     security_origin = SecurityOrigin::CreateUniqueOpaque();
@@ -511,8 +511,7 @@
     WebTreeScopeType scope,
     const WebString& name,
     const WebString& fallback_name,
-    WebSandboxFlags sandbox_flags,
-    const ParsedFeaturePolicy& container_policy,
+    const FramePolicy&,
     const WebFrameOwnerProperties& frame_owner_properties,
     FrameOwnerElementType owner_type) {
   return CreateLocalChild(*parent, scope);
diff --git a/third_party/blink/renderer/core/frame/frame_test_helpers.h b/third_party/blink/renderer/core/frame/frame_test_helpers.h
index db540bd..8068ecb6 100644
--- a/third_party/blink/renderer/core/frame/frame_test_helpers.h
+++ b/third_party/blink/renderer/core/frame/frame_test_helpers.h
@@ -374,8 +374,7 @@
                                   WebTreeScopeType,
                                   const WebString& name,
                                   const WebString& fallback_name,
-                                  WebSandboxFlags,
-                                  const ParsedFeaturePolicy&,
+                                  const FramePolicy&,
                                   const WebFrameOwnerProperties&,
                                   FrameOwnerElementType) override;
   void DidStartLoading() override;
diff --git a/third_party/blink/renderer/core/frame/local_frame_client.h b/third_party/blink/renderer/core/frame/local_frame_client.h
index 0cccfe4..693f35a9 100644
--- a/third_party/blink/renderer/core/frame/local_frame_client.h
+++ b/third_party/blink/renderer/core/frame/local_frame_client.h
@@ -342,9 +342,7 @@
   virtual void DidEnforceInsecureRequestPolicy(WebInsecureRequestPolicy) {}
   virtual void DidEnforceInsecureNavigationsSet(const std::vector<unsigned>&) {}
 
-  virtual void DidChangeFramePolicy(Frame* child_frame,
-                                    SandboxFlags,
-                                    const ParsedFeaturePolicy&) {}
+  virtual void DidChangeFramePolicy(Frame* child_frame, const FramePolicy&) {}
 
   virtual void DidSetFramePolicyHeaders(
       SandboxFlags,
diff --git a/third_party/blink/renderer/core/frame/local_frame_view.cc b/third_party/blink/renderer/core/frame/local_frame_view.cc
index f0e2a6d..afe1f27 100644
--- a/third_party/blink/renderer/core/frame/local_frame_view.cc
+++ b/third_party/blink/renderer/core/frame/local_frame_view.cc
@@ -2589,10 +2589,8 @@
   // that don't for the purposes of hit testing. For example, an empty div
   // will not draw content but needs to create a layer to ensure scroll events
   // do not pass through it.
-  if (layer->PaintsContentOrHitTest() ||
-      layer->GetHitTestableWithoutDrawsContent()) {
+  if (layer->PaintsContentOrHitTest() || layer->GetHitTestable())
     RecordGraphicsLayerAsForeignLayer(context, layer);
-  }
 
   if (auto* contents_layer = layer->ContentsLayer()) {
     RecordForeignLayer(context, DisplayItem::kForeignLayerContentsWrapper,
diff --git a/third_party/blink/renderer/core/frame/remote_frame_owner.cc b/third_party/blink/renderer/core/frame/remote_frame_owner.cc
index b35efa69..e3adcd17 100644
--- a/third_party/blink/renderer/core/frame/remote_frame_owner.cc
+++ b/third_party/blink/renderer/core/frame/remote_frame_owner.cc
@@ -17,11 +17,10 @@
 namespace blink {
 
 RemoteFrameOwner::RemoteFrameOwner(
-    SandboxFlags flags,
-    const ParsedFeaturePolicy& container_policy,
+    const FramePolicy& frame_policy,
     const WebFrameOwnerProperties& frame_owner_properties,
     FrameOwnerElementType frame_owner_element_type)
-    : sandbox_flags_(flags),
+    : frame_policy_(frame_policy),
       browsing_context_container_name_(
           static_cast<String>(frame_owner_properties.name)),
       scrolling_(
@@ -33,7 +32,6 @@
       is_display_none_(frame_owner_properties.is_display_none),
       needs_occlusion_tracking_(false),
       required_csp_(frame_owner_properties.required_csp),
-      container_policy_(container_policy),
       frame_owner_element_type_(frame_owner_element_type) {}
 
 void RemoteFrameOwner::Trace(blink::Visitor* visitor) {
diff --git a/third_party/blink/renderer/core/frame/remote_frame_owner.h b/third_party/blink/renderer/core/frame/remote_frame_owner.h
index 550622c..31afc440 100644
--- a/third_party/blink/renderer/core/frame/remote_frame_owner.h
+++ b/third_party/blink/renderer/core/frame/remote_frame_owner.h
@@ -6,6 +6,7 @@
 #define THIRD_PARTY_BLINK_RENDERER_CORE_FRAME_REMOTE_FRAME_OWNER_H_
 
 #include "third_party/blink/public/common/frame/frame_owner_element_type.h"
+#include "third_party/blink/public/common/frame/frame_policy.h"
 #include "third_party/blink/public/web/web_frame_owner_properties.h"
 #include "third_party/blink/renderer/core/core_export.h"
 #include "third_party/blink/renderer/core/frame/frame_owner.h"
@@ -25,8 +26,7 @@
   USING_GARBAGE_COLLECTED_MIXIN(RemoteFrameOwner);
 
  public:
-  RemoteFrameOwner(SandboxFlags,
-                   const ParsedFeaturePolicy&,
+  RemoteFrameOwner(const FramePolicy&,
                    const WebFrameOwnerProperties&,
                    FrameOwnerElementType frame_owner_element_type);
 
@@ -34,8 +34,7 @@
   Frame* ContentFrame() const override { return frame_.Get(); }
   void SetContentFrame(Frame&) override;
   void ClearContentFrame() override;
-  SandboxFlags GetSandboxFlags() const override { return sandbox_flags_; }
-  void SetSandboxFlags(SandboxFlags flags) { sandbox_flags_ = flags; }
+  const FramePolicy& GetFramePolicy() const override { return frame_policy_; }
   void AddResourceTiming(const ResourceTimingInfo&) override;
   void DispatchLoad() override;
   bool CanRenderFallbackContent() const override {
@@ -55,11 +54,11 @@
   bool AllowPaymentRequest() const override { return allow_payment_request_; }
   bool IsDisplayNone() const override { return is_display_none_; }
   AtomicString RequiredCsp() const override { return required_csp_; }
-  const ParsedFeaturePolicy& ContainerPolicy() const override {
-    return container_policy_;
-  }
   bool ShouldLazyLoadChildren() const final;
 
+  void SetFramePolicy(const FramePolicy& frame_policy) {
+    frame_policy_ = frame_policy;
+  }
   void SetBrowsingContextContainerName(const WebString& name) {
     browsing_context_container_name_ = name;
   }
@@ -78,9 +77,6 @@
   void SetRequiredCsp(const WebString& required_csp) {
     required_csp_ = required_csp;
   }
-  void SetContainerPolicy(const ParsedFeaturePolicy& container_policy) {
-    container_policy_ = container_policy;
-  }
 
   void Trace(blink::Visitor*) override;
 
@@ -91,7 +87,7 @@
   bool IsRemote() const override { return true; }
 
   Member<Frame> frame_;
-  SandboxFlags sandbox_flags_;
+  FramePolicy frame_policy_;
   AtomicString browsing_context_container_name_;
   ScrollbarMode scrolling_;
   int margin_width_;
@@ -101,7 +97,6 @@
   bool is_display_none_;
   bool needs_occlusion_tracking_;
   WebString required_csp_;
-  ParsedFeaturePolicy container_policy_;
   const FrameOwnerElementType frame_owner_element_type_;
 };
 
diff --git a/third_party/blink/renderer/core/frame/visual_viewport.cc b/third_party/blink/renderer/core/frame/visual_viewport.cc
index b0322c6f..0c0e308 100644
--- a/third_party/blink/renderer/core/frame/visual_viewport.cc
+++ b/third_party/blink/renderer/core/frame/visual_viewport.cc
@@ -700,6 +700,7 @@
         scrollbar_layer_group->layer.get(),
         /*prevent_contents_opaque_changes=*/false);
     scrollbar_graphics_layer->SetDrawsContent(false);
+    scrollbar_graphics_layer->SetHitTestable(false);
     scrollbar_layer_group->scrollbar_layer->SetScrollElementId(
         inner_viewport_scroll_layer_->CcLayer()->element_id());
   }
diff --git a/third_party/blink/renderer/core/frame/web_local_frame_impl.cc b/third_party/blink/renderer/core/frame/web_local_frame_impl.cc
index 8f45c37..5032acc 100644
--- a/third_party/blink/renderer/core/frame/web_local_frame_impl.cc
+++ b/third_party/blink/renderer/core/frame/web_local_frame_impl.cc
@@ -1615,11 +1615,10 @@
     InterfaceRegistry* interface_registry,
     mojo::ScopedMessagePipeHandle document_interface_broker_handle,
     WebFrame* previous_frame,
-    WebSandboxFlags flags,
-    ParsedFeaturePolicy container_policy) {
+    const FramePolicy& frame_policy) {
   return WebLocalFrameImpl::CreateProvisional(
       client, interface_registry, std::move(document_interface_broker_handle),
-      previous_frame, flags, container_policy);
+      previous_frame, frame_policy);
 }
 
 WebLocalFrameImpl* WebLocalFrameImpl::Create(
@@ -1664,8 +1663,7 @@
     blink::InterfaceRegistry* interface_registry,
     mojo::ScopedMessagePipeHandle document_interface_broker_handle,
     WebFrame* previous_web_frame,
-    WebSandboxFlags flags,
-    ParsedFeaturePolicy container_policy) {
+    const FramePolicy& frame_policy) {
   DCHECK(client);
   WebLocalFrameImpl* web_frame = MakeGarbageCollected<WebLocalFrameImpl>(
       previous_web_frame, client, interface_registry,
@@ -1692,13 +1690,12 @@
   new_frame->SetOwner(previous_frame->Owner());
   if (auto* remote_frame_owner =
           DynamicTo<RemoteFrameOwner>(new_frame->Owner())) {
-    remote_frame_owner->SetSandboxFlags(static_cast<SandboxFlags>(flags));
-    remote_frame_owner->SetContainerPolicy(container_policy);
+    remote_frame_owner->SetFramePolicy(frame_policy);
   } else if (!new_frame->Owner()) {
     // Provisional main frames need to force sandbox flags.  This is necessary
     // to inherit sandbox flags when a sandboxed frame does a window.open()
     // which triggers a cross-process navigation.
-    new_frame->Loader().ForceSandboxFlags(static_cast<SandboxFlags>(flags));
+    new_frame->Loader().ForceSandboxFlags(frame_policy.sandbox_flags);
     // If there is an opener (even disowned), the opener policies must be
     // inherited the same way as sandbox flag.
     new_frame->SetOpenerFeatureState(previous_frame->OpenerFeatureState());
@@ -1817,14 +1814,12 @@
   // solution. subResourceAttributeName returns just one attribute name. The
   // element might not have the attribute, and there might be other attributes
   // which can identify the element.
-  WebLocalFrameImpl* webframe_child =
-      To<WebLocalFrameImpl>(client_->CreateChildFrame(
-          this, scope, name,
-          owner_element->getAttribute(
-              owner_element->SubResourceAttributeName()),
-          static_cast<WebSandboxFlags>(owner_element->GetSandboxFlags()),
-          owner_element->ContainerPolicy(), owner_properties,
-          owner_element->OwnerType()));
+  WebLocalFrameImpl* webframe_child = To<WebLocalFrameImpl>(
+      client_->CreateChildFrame(this, scope, name,
+                                owner_element->getAttribute(
+                                    owner_element->SubResourceAttributeName()),
+                                owner_element->GetFramePolicy(),
+                                owner_properties, owner_element->OwnerType()));
   if (!webframe_child)
     return nullptr;
 
diff --git a/third_party/blink/renderer/core/frame/web_local_frame_impl.h b/third_party/blink/renderer/core/frame/web_local_frame_impl.h
index ef23363..ab46b26 100644
--- a/third_party/blink/renderer/core/frame/web_local_frame_impl.h
+++ b/third_party/blink/renderer/core/frame/web_local_frame_impl.h
@@ -359,8 +359,7 @@
                                               InterfaceRegistry*,
                                               mojo::ScopedMessagePipeHandle,
                                               WebFrame*,
-                                              WebSandboxFlags,
-                                              ParsedFeaturePolicy);
+                                              const FramePolicy&);
 
   WebLocalFrameImpl(WebTreeScopeType,
                     WebLocalFrameClient*,
diff --git a/third_party/blink/renderer/core/html/html_frame_element_base.cc b/third_party/blink/renderer/core/html/html_frame_element_base.cc
index bcd8df0..7eb0252 100644
--- a/third_party/blink/renderer/core/html/html_frame_element_base.cc
+++ b/third_party/blink/renderer/core/html/html_frame_element_base.cc
@@ -138,7 +138,8 @@
 scoped_refptr<const SecurityOrigin>
 HTMLFrameElementBase::GetOriginForFeaturePolicy() const {
   // Sandboxed frames have a unique origin.
-  if ((GetSandboxFlags() & WebSandboxFlags::kOrigin) != WebSandboxFlags::kNone)
+  if ((GetFramePolicy().sandbox_flags & WebSandboxFlags::kOrigin) !=
+      WebSandboxFlags::kNone)
     return SecurityOrigin::CreateUniqueOpaque();
 
   // If the frame will inherit its origin from the owner, then use the owner's
diff --git a/third_party/blink/renderer/core/html/html_frame_element_test.cc b/third_party/blink/renderer/core/html/html_frame_element_test.cc
index 2a9b23b..4be6b74 100644
--- a/third_party/blink/renderer/core/html/html_frame_element_test.cc
+++ b/third_party/blink/renderer/core/html/html_frame_element_test.cc
@@ -26,7 +26,7 @@
   frame_element->UpdateContainerPolicyForTests();
 
   const ParsedFeaturePolicy& container_policy =
-      frame_element->ContainerPolicy();
+      frame_element->GetFramePolicy().container_policy;
   EXPECT_EQ(1UL, container_policy.size());
   // Fullscreen should be disabled in this frame
   EXPECT_EQ(mojom::FeaturePolicyFeature::kFullscreen,
diff --git a/third_party/blink/renderer/core/html/html_frame_owner_element.cc b/third_party/blink/renderer/core/html/html_frame_owner_element.cc
index 64eeea9..2e4b86d 100644
--- a/third_party/blink/renderer/core/html/html_frame_owner_element.cc
+++ b/third_party/blink/renderer/core/html/html_frame_owner_element.cc
@@ -145,7 +145,6 @@
     : HTMLElement(tag_name, document),
       content_frame_(nullptr),
       embedded_content_view_(nullptr),
-      sandbox_flags_(WebSandboxFlags::kNone),
       should_lazy_load_children_(DoesParentAllowLazyLoadingChildren(document)) {
 }
 
@@ -246,16 +245,16 @@
 }
 
 void HTMLFrameOwnerElement::SetSandboxFlags(SandboxFlags flags) {
-  sandbox_flags_ = flags;
+  frame_policy_.sandbox_flags = flags;
   // Recalculate the container policy in case the allow-same-origin flag has
   // changed.
-  container_policy_ = ConstructContainerPolicy(nullptr);
+  frame_policy_.container_policy = ConstructContainerPolicy(nullptr);
 
   // Don't notify about updates if ContentFrame() is null, for example when
   // the subframe hasn't been created yet.
   if (ContentFrame()) {
-    GetDocument().GetFrame()->Client()->DidChangeFramePolicy(
-        ContentFrame(), sandbox_flags_, container_policy_);
+    GetDocument().GetFrame()->Client()->DidChangeFramePolicy(ContentFrame(),
+                                                             frame_policy_);
   }
 }
 
@@ -272,12 +271,12 @@
 }
 
 void HTMLFrameOwnerElement::UpdateContainerPolicy(Vector<String>* messages) {
-  container_policy_ = ConstructContainerPolicy(messages);
+  frame_policy_.container_policy = ConstructContainerPolicy(messages);
   // Don't notify about updates if ContentFrame() is null, for example when
   // the subframe hasn't been created yet.
   if (ContentFrame()) {
-    GetDocument().GetFrame()->Client()->DidChangeFramePolicy(
-        ContentFrame(), sandbox_flags_, container_policy_);
+    GetDocument().GetFrame()->Client()->DidChangeFramePolicy(ContentFrame(),
+                                                             frame_policy_);
   }
 }
 
@@ -308,10 +307,6 @@
   DispatchScopedEvent(*Event::Create(event_type_names::kLoad));
 }
 
-const ParsedFeaturePolicy& HTMLFrameOwnerElement::ContainerPolicy() const {
-  return container_policy_;
-}
-
 Document* HTMLFrameOwnerElement::getSVGDocument(
     ExceptionState& exception_state) const {
   Document* doc = contentDocument();
diff --git a/third_party/blink/renderer/core/html/html_frame_owner_element.h b/third_party/blink/renderer/core/html/html_frame_owner_element.h
index b733a12..ca91bec 100644
--- a/third_party/blink/renderer/core/html/html_frame_owner_element.h
+++ b/third_party/blink/renderer/core/html/html_frame_owner_element.h
@@ -100,7 +100,7 @@
   void ClearContentFrame() final;
   void AddResourceTiming(const ResourceTimingInfo&) final;
   void DispatchLoad() final;
-  SandboxFlags GetSandboxFlags() const final { return sandbox_flags_; }
+  const FramePolicy& GetFramePolicy() const final { return frame_policy_; }
   bool CanRenderFallbackContent() const override { return false; }
   void RenderFallbackContent(Frame*) override {}
   void IntrinsicSizingInfoChanged() override {}
@@ -115,7 +115,6 @@
   bool AllowPaymentRequest() const override { return false; }
   bool IsDisplayNone() const override { return !embedded_content_view_; }
   AtomicString RequiredCsp() const override { return g_null_atom; }
-  const ParsedFeaturePolicy& ContainerPolicy() const override;
   bool ShouldLazyLoadChildren() const final;
 
   // For unit tests, manually trigger the UpdateContainerPolicy method.
@@ -177,9 +176,7 @@
 
   Member<Frame> content_frame_;
   Member<EmbeddedContentView> embedded_content_view_;
-  SandboxFlags sandbox_flags_;
-
-  ParsedFeaturePolicy container_policy_;
+  FramePolicy frame_policy_;
 
   Member<LazyLoadFrameObserver> lazy_load_frame_observer_;
   bool should_lazy_load_children_;
diff --git a/third_party/blink/renderer/core/html/html_iframe_element.cc b/third_party/blink/renderer/core/html/html_iframe_element.cc
index 517043c..758dea2 100644
--- a/third_party/blink/renderer/core/html/html_iframe_element.cc
+++ b/third_party/blink/renderer/core/html/html_iframe_element.cc
@@ -91,7 +91,8 @@
 DOMFeaturePolicy* HTMLIFrameElement::featurePolicy() {
   if (!policy_) {
     policy_ = MakeGarbageCollected<IFramePolicy>(
-        &GetDocument(), ContainerPolicy(), GetOriginForFeaturePolicy());
+        &GetDocument(), GetFramePolicy().container_policy,
+        GetOriginForFeaturePolicy());
   }
   return policy_.Get();
 }
diff --git a/third_party/blink/renderer/core/html/html_iframe_element_test.cc b/third_party/blink/renderer/core/html/html_iframe_element_test.cc
index caf0d967..ad81f31 100644
--- a/third_party/blink/renderer/core/html/html_iframe_element_test.cc
+++ b/third_party/blink/renderer/core/html/html_iframe_element_test.cc
@@ -176,7 +176,7 @@
   frame_element->UpdateContainerPolicyForTests();
 
   const ParsedFeaturePolicy& container_policy =
-      frame_element->ContainerPolicy();
+      frame_element->GetFramePolicy().container_policy;
   EXPECT_EQ(0UL, container_policy.size());
 }
 
@@ -195,7 +195,7 @@
   frame_element->UpdateContainerPolicyForTests();
 
   const ParsedFeaturePolicy& container_policy1 =
-      frame_element->ContainerPolicy();
+      frame_element->GetFramePolicy().container_policy;
 
   EXPECT_EQ(1UL, container_policy1.size());
   EXPECT_EQ(mojom::FeaturePolicyFeature::kFullscreen,
@@ -209,7 +209,7 @@
   frame_element->UpdateContainerPolicyForTests();
 
   const ParsedFeaturePolicy& container_policy2 =
-      frame_element->ContainerPolicy();
+      frame_element->GetFramePolicy().container_policy;
   EXPECT_EQ(2UL, container_policy2.size());
   EXPECT_TRUE(container_policy2[0].feature ==
                   mojom::FeaturePolicyFeature::kFullscreen ||
@@ -243,7 +243,7 @@
   frame_element->UpdateContainerPolicyForTests();
 
   const ParsedFeaturePolicy& container_policy =
-      frame_element->ContainerPolicy();
+      frame_element->GetFramePolicy().container_policy;
 
   EXPECT_EQ(expected_number_of_sandbox_features, container_policy.size());
 }
@@ -265,7 +265,7 @@
   frame_element->UpdateContainerPolicyForTests();
 
   const ParsedFeaturePolicy& container_policy =
-      frame_element->ContainerPolicy();
+      frame_element->GetFramePolicy().container_policy;
 
   EXPECT_EQ(expected_number_of_sandbox_features + 1, container_policy.size());
   const auto& container_policy_item = std::find_if(
@@ -301,7 +301,7 @@
   frame_element->UpdateContainerPolicyForTests();
 
   const ParsedFeaturePolicy& container_policy =
-      frame_element->ContainerPolicy();
+      frame_element->GetFramePolicy().container_policy;
 
   EXPECT_EQ(expected_number_of_sandbox_features + 1, container_policy.size());
   const auto& container_policy_item = std::find_if(
diff --git a/third_party/blink/renderer/core/html/media/picture_in_picture_interstitial.cc b/third_party/blink/renderer/core/html/media/picture_in_picture_interstitial.cc
index 4224493..26e70c15f 100644
--- a/third_party/blink/renderer/core/html/media/picture_in_picture_interstitial.cc
+++ b/third_party/blink/renderer/core/html/media/picture_in_picture_interstitial.cc
@@ -98,6 +98,7 @@
 
   DCHECK(GetVideoElement().CcLayer());
   GetVideoElement().CcLayer()->SetIsDrawable(false);
+  GetVideoElement().CcLayer()->SetHitTestable(false);
 }
 
 void PictureInPictureInterstitial::Hide() {
@@ -112,8 +113,10 @@
   interstitial_timer_.StartOneShot(kPictureInPictureHiddenAnimationSeconds,
                                    FROM_HERE);
 
-  if (GetVideoElement().CcLayer())
+  if (GetVideoElement().CcLayer()) {
     GetVideoElement().CcLayer()->SetIsDrawable(true);
+    GetVideoElement().CcLayer()->SetHitTestable(true);
+  }
 }
 
 Node::InsertionNotificationRequest PictureInPictureInterstitial::InsertedInto(
diff --git a/third_party/blink/renderer/core/inspector/inspector_overlay_agent.cc b/third_party/blink/renderer/core/inspector/inspector_overlay_agent.cc
index 1156669..5e0b0a0 100644
--- a/third_party/blink/renderer/core/inspector/inspector_overlay_agent.cc
+++ b/third_party/blink/renderer/core/inspector/inspector_overlay_agent.cc
@@ -208,6 +208,7 @@
     if (RuntimeEnabledFeatures::CompositeAfterPaintEnabled()) {
       layer_ = cc::PictureLayer::Create(this);
       layer_->SetIsDrawable(true);
+      layer_->SetHitTestable(true);
     }
   }
   ~InspectorPageOverlayDelegate() override {
diff --git a/third_party/blink/renderer/core/loader/frame_loader.cc b/third_party/blink/renderer/core/loader/frame_loader.cc
index c3cf805..b735a3bf 100644
--- a/third_party/blink/renderer/core/loader/frame_loader.cc
+++ b/third_party/blink/renderer/core/loader/frame_loader.cc
@@ -714,8 +714,8 @@
       return false;
 
     if (frame_->Owner() &&
-        ((frame_->Owner()->GetSandboxFlags() & WebSandboxFlags::kOrigin) !=
-         WebSandboxFlags::kNone))
+        ((frame_->Owner()->GetFramePolicy().sandbox_flags &
+          WebSandboxFlags::kOrigin) != WebSandboxFlags::kNone))
       return false;
 
     frame_->GetDocument()->ProcessJavaScriptUrl(
@@ -1635,7 +1635,7 @@
 SandboxFlags FrameLoader::EffectiveSandboxFlags() const {
   SandboxFlags flags = forced_sandbox_flags_;
   if (FrameOwner* frame_owner = frame_->Owner())
-    flags |= frame_owner->GetSandboxFlags();
+    flags |= frame_owner->GetFramePolicy().sandbox_flags;
   // Frames need to inherit the sandbox flags of their parent frame.
   if (Frame* parent_frame = frame_->Tree().Parent())
     flags |= parent_frame->GetSecurityContext()->GetSandboxFlags();
diff --git a/third_party/blink/renderer/core/loader/private/prerender_handle.cc b/third_party/blink/renderer/core/loader/private/prerender_handle.cc
index 6208a53..bc1ccb7 100644
--- a/third_party/blink/renderer/core/loader/private/prerender_handle.cc
+++ b/third_party/blink/renderer/core/loader/private/prerender_handle.cc
@@ -35,6 +35,7 @@
 #include "third_party/blink/renderer/core/frame/local_frame.h"
 #include "third_party/blink/renderer/core/loader/frame_loader.h"
 #include "third_party/blink/renderer/core/loader/prerenderer_client.h"
+#include "third_party/blink/renderer/platform/heap/heap.h"
 #include "third_party/blink/renderer/platform/prerender.h"
 #include "third_party/blink/renderer/platform/weborigin/security_policy.h"
 
@@ -50,7 +51,7 @@
   if (!document.GetFrame())
     return nullptr;
 
-  Prerender* prerender = Prerender::Create(
+  auto* prerender = MakeGarbageCollected<Prerender>(
       client, url, prerender_rel_types,
       SecurityPolicy::GenerateReferrer(document.GetReferrerPolicy(), url,
                                        document.OutgoingReferrer()));
diff --git a/third_party/blink/renderer/core/page/scrolling/scrolling_coordinator.cc b/third_party/blink/renderer/core/page/scrolling/scrolling_coordinator.cc
index b6d68c7..fd5b415 100644
--- a/third_party/blink/renderer/core/page/scrolling/scrolling_coordinator.cc
+++ b/third_party/blink/renderer/core/page/scrolling/scrolling_coordinator.cc
@@ -397,6 +397,7 @@
 
   scrollbar_graphics_layer->SetContentsToCcLayer(nullptr, false);
   scrollbar_graphics_layer->SetDrawsContent(true);
+  scrollbar_graphics_layer->SetHitTestable(true);
 }
 
 static void SetupScrollbarLayer(
@@ -416,6 +417,7 @@
       scrollbar_layer_group->layer.get(),
       /*prevent_contents_opaque_changes=*/false);
   scrollbar_graphics_layer->SetDrawsContent(false);
+  scrollbar_graphics_layer->SetHitTestable(false);
 }
 
 void ScrollingCoordinator::AddScrollbarLayerGroup(
diff --git a/third_party/blink/renderer/core/paint/compositing/composited_layer_mapping.cc b/third_party/blink/renderer/core/paint/compositing/composited_layer_mapping.cc
index 9d8833c..cc8339c 100644
--- a/third_party/blink/renderer/core/paint/compositing/composited_layer_mapping.cc
+++ b/third_party/blink/renderer/core/paint/compositing/composited_layer_mapping.cc
@@ -268,7 +268,7 @@
       CreateGraphicsLayer(owning_layer_.GetCompositingReasons(),
                           owning_layer_.GetSquashingDisallowedReasons());
 
-  UpdateHitTestableWithoutDrawsContent(true);
+  graphics_layer_->SetHitTestable(true);
   UpdateOpacity(GetLayoutObject().StyleRef());
   UpdateTransform(GetLayoutObject().StyleRef());
   if (!RuntimeEnabledFeatures::BlinkGenPropertyTreesEnabled())
@@ -296,11 +296,6 @@
   scrolling_contents_layer_ = nullptr;
 }
 
-void CompositedLayerMapping::UpdateHitTestableWithoutDrawsContent(
-    const bool& should_hit_test) {
-  graphics_layer_->SetHitTestableWithoutDrawsContent(should_hit_test);
-}
-
 void CompositedLayerMapping::UpdateOpacity(const ComputedStyle& style) {
   graphics_layer_->SetOpacity(CompositingOpacity(style.Opacity()));
 }
@@ -1342,6 +1337,11 @@
   // layers.
   bool contents_visible = owning_layer_.HasVisibleContent() ||
                           HasVisibleNonCompositingDescendant(&owning_layer_);
+  // TODO(sunxd): Investigate and possibly implement computing hit test regions
+  // in PaintTouchActionRects code path, so that cc has correct pointer-events
+  // information.
+  // For now, there is no need to set graphics_layer_'s hit testable bit here,
+  // because it is always hit testable from cc's perspective.
   graphics_layer_->SetContentsVisible(contents_visible);
 
   // In BGPT mode, we do not need to update the backface visibility here, as it
@@ -2175,7 +2175,9 @@
       if (layer->HasContentsLayer())
         layer->SetContentsRect(IntRect(IntPoint(), frame_rect.Size()));
     }
-    layer->SetDrawsContent(h_bar && !layer->HasContentsLayer());
+    bool h_bar_visible = h_bar && !layer->HasContentsLayer();
+    layer->SetDrawsContent(h_bar_visible);
+    layer->SetHitTestable(h_bar_visible);
   }
 
   if (GraphicsLayer* layer = LayerForVerticalScrollbar()) {
@@ -2188,7 +2190,9 @@
       if (layer->HasContentsLayer())
         layer->SetContentsRect(IntRect(IntPoint(), frame_rect.Size()));
     }
-    layer->SetDrawsContent(v_bar && !layer->HasContentsLayer());
+    bool v_bar_visible = v_bar && !layer->HasContentsLayer();
+    layer->SetDrawsContent(v_bar_visible);
+    layer->SetHitTestable(v_bar_visible);
   }
 
   if (GraphicsLayer* layer = LayerForScrollCorner()) {
@@ -2199,6 +2203,7 @@
         ToIntSize(scroll_corner_and_resizer.Location()));
     layer->SetSize(gfx::Size(scroll_corner_and_resizer.Size()));
     layer->SetDrawsContent(!scroll_corner_and_resizer.IsEmpty());
+    layer->SetHitTestable(!scroll_corner_and_resizer.IsEmpty());
   }
 }
 
@@ -2389,7 +2394,7 @@
     if (!foreground_layer_) {
       foreground_layer_ =
           CreateGraphicsLayer(CompositingReason::kLayerForForeground);
-      foreground_layer_->SetHitTestableWithoutDrawsContent(true);
+      foreground_layer_->SetHitTestable(true);
       layer_changed = true;
     }
   } else if (foreground_layer_) {
@@ -2477,12 +2482,13 @@
       scrolling_layer_ =
           CreateGraphicsLayer(CompositingReason::kLayerForScrollingContainer);
       scrolling_layer_->SetDrawsContent(false);
+      scrolling_layer_->SetHitTestable(false);
       scrolling_layer_->SetMasksToBounds(true);
 
       // Inner layer which renders the content that scrolls.
       scrolling_contents_layer_ =
           CreateGraphicsLayer(CompositingReason::kLayerForScrollingContents);
-      scrolling_contents_layer_->SetHitTestableWithoutDrawsContent(true);
+      scrolling_contents_layer_->SetHitTestable(true);
 
       auto element_id = scrollable_area->GetCompositorElementId();
       scrolling_contents_layer_->SetElementId(element_id);
@@ -2639,6 +2645,7 @@
       squashing_layer_ =
           CreateGraphicsLayer(CompositingReason::kLayerForSquashingContents);
       squashing_layer_->SetDrawsContent(true);
+      squashing_layer_->SetHitTestable(true);
       layers_changed = true;
     }
 
diff --git a/third_party/blink/renderer/core/paint/compositing/composited_layer_mapping.h b/third_party/blink/renderer/core/paint/compositing/composited_layer_mapping.h
index be465bb8..3f62979 100644
--- a/third_party/blink/renderer/core/paint/compositing/composited_layer_mapping.h
+++ b/third_party/blink/renderer/core/paint/compositing/composited_layer_mapping.h
@@ -438,7 +438,6 @@
   // Result is transform origin in pixels.
   FloatPoint3D ComputeTransformOrigin(const IntRect& border_box) const;
 
-  void UpdateHitTestableWithoutDrawsContent(const bool&);
   void UpdateOpacity(const ComputedStyle&);
   void UpdateTransform(const ComputedStyle&);
   void UpdateLayerBlendMode(const ComputedStyle&);
diff --git a/third_party/blink/renderer/core/paint/compositing/composited_layer_mapping_test.cc b/third_party/blink/renderer/core/paint/compositing/composited_layer_mapping_test.cc
index 86c9f02..8dff6ab1 100644
--- a/third_party/blink/renderer/core/paint/compositing/composited_layer_mapping_test.cc
+++ b/third_party/blink/renderer/core/paint/compositing/composited_layer_mapping_test.cc
@@ -1045,7 +1045,7 @@
       mapping->ForegroundLayer()->PaintingPhase());
   // Regression test for crbug.com/767908: a foreground layer should also
   // participates hit testing.
-  EXPECT_TRUE(mapping->ForegroundLayer()->GetHitTestableWithoutDrawsContent());
+  EXPECT_TRUE(mapping->ForegroundLayer()->GetHitTestable());
 
   Element* negative_composited_child =
       GetDocument().getElementById("negative-composited-child");
diff --git a/third_party/blink/renderer/core/paint/html_canvas_painter.cc b/third_party/blink/renderer/core/paint/html_canvas_painter.cc
index d0f90bc..18733ed 100644
--- a/third_party/blink/renderer/core/paint/html_canvas_painter.cc
+++ b/third_party/blink/renderer/core/paint/html_canvas_painter.cc
@@ -46,6 +46,7 @@
           gfx::Vector2dF(pixel_snapped_rect.X(), pixel_snapped_rect.Y()));
       layer->SetBounds(gfx::Size(pixel_snapped_rect.Size()));
       layer->SetIsDrawable(true);
+      layer->SetHitTestable(true);
       RecordForeignLayer(context, DisplayItem::kForeignLayerCanvas, layer);
       return;
     }
diff --git a/third_party/blink/renderer/core/paint/video_painter.cc b/third_party/blink/renderer/core/paint/video_painter.cc
index b5764b44..3a1ddcd 100644
--- a/third_party/blink/renderer/core/paint/video_painter.cc
+++ b/third_party/blink/renderer/core/paint/video_painter.cc
@@ -55,6 +55,7 @@
           gfx::Vector2dF(snapped_replaced_rect.X(), snapped_replaced_rect.Y()));
       layer->SetBounds(gfx::Size(snapped_replaced_rect.Size()));
       layer->SetIsDrawable(true);
+      layer->SetHitTestable(true);
       RecordForeignLayer(context, DisplayItem::kForeignLayerVideo, layer);
       return;
     }
diff --git a/third_party/blink/renderer/core/paint/video_painter_test.cc b/third_party/blink/renderer/core/paint/video_painter_test.cc
index 1a6fe96..7cdc666 100644
--- a/third_party/blink/renderer/core/paint/video_painter_test.cc
+++ b/third_party/blink/renderer/core/paint/video_painter_test.cc
@@ -33,6 +33,7 @@
     client_->ReadyStateChanged();
     layer_ = cc::Layer::Create();
     layer_->SetIsDrawable(true);
+    layer_->SetHitTestable(true);
     client_->SetCcLayer(layer_.get());
     return LoadTiming::kImmediate;
   }
diff --git a/third_party/blink/renderer/devtools/front_end/resources/BackgroundServiceView.js b/third_party/blink/renderer/devtools/front_end/resources/BackgroundServiceView.js
index c9b14710..4021f43 100644
--- a/third_party/blink/renderer/devtools/front_end/resources/BackgroundServiceView.js
+++ b/third_party/blink/renderer/devtools/front_end/resources/BackgroundServiceView.js
@@ -36,6 +36,9 @@
     /** @type {?UI.ToolbarCheckbox} */
     this._originCheckbox = null;
 
+    /** @type {?UI.ToolbarButton} */
+    this._saveButton = null;
+
     /** @const {!UI.Toolbar} */
     this._toolbar = new UI.Toolbar('background-service-toolbar', this.contentElement);
     this._setupToolbar();
@@ -68,20 +71,27 @@
    */
   async _setupToolbar() {
     this._recordButton =
-        new UI.ToolbarToggle(Common.UIString('Toggle Record'), 'largeicon-start-recording', 'largeicon-stop-recording');
+        new UI.ToolbarToggle(ls`Toggle Record`, 'largeicon-start-recording', 'largeicon-stop-recording');
     this._recordButton.addEventListener(UI.ToolbarButton.Events.Click, () => this._toggleRecording());
     this._recordButton.setToggleWithRedColor(true);
     this._toolbar.appendToolbarItem(this._recordButton);
 
-    const clearButton = new UI.ToolbarButton(Common.UIString('Clear'), 'largeicon-clear');
+    const clearButton = new UI.ToolbarButton(ls`Clear`, 'largeicon-clear');
     clearButton.addEventListener(UI.ToolbarButton.Events.Click, () => this._clearEvents());
     this._toolbar.appendToolbarItem(clearButton);
 
     this._toolbar.appendSeparator();
 
     this._originCheckbox =
-        new UI.ToolbarCheckbox(Common.UIString('Show events from other domains'), undefined, () => this._refreshView());
+        new UI.ToolbarCheckbox(ls`Show events from other domains`, undefined, () => this._refreshView());
     this._toolbar.appendToolbarItem(this._originCheckbox);
+
+    this._toolbar.appendSeparator();
+
+    this._saveButton = new UI.ToolbarButton(ls`Save events`, 'largeicon-download');
+    this._saveButton.addEventListener(UI.ToolbarButton.Events.Click, () => this._saveToFile());
+    this._saveButton.setEnabled(false);
+    this._toolbar.appendToolbarItem(this._saveButton);
   }
 
   /**
@@ -100,6 +110,7 @@
   _clearView() {
     this._dataGrid.rootNode().removeChildren();
     this._showPreview(null);
+    this._saveButton.setEnabled(false);
   }
 
   /**
@@ -151,6 +162,9 @@
     const data = this._createEventData(serviceEvent);
     const dataNode = new Resources.BackgroundServiceView.EventDataNode(data, serviceEvent.eventMetadata);
     this._dataGrid.rootNode().appendChild(dataNode);
+
+    // There's at least one event. So we can allow saving the events.
+    this._saveButton.setEnabled(true);
   }
 
   /**
@@ -158,12 +172,12 @@
    */
   _createDataGrid() {
     const columns = /** @type {!Array<!DataGrid.DataGrid.ColumnDescriptor>} */ ([
-      {id: 'id', title: Common.UIString('#'), weight: 1},
-      {id: 'timestamp', title: Common.UIString('Timestamp'), weight: 8},
-      {id: 'origin', title: Common.UIString('Origin'), weight: 10},
-      {id: 'swSource', title: Common.UIString('SW Source'), weight: 4},
-      {id: 'eventName', title: Common.UIString('Event'), weight: 10},
-      {id: 'instanceId', title: Common.UIString('Instance ID'), weight: 10},
+      {id: 'id', title: ls`#`, weight: 1},
+      {id: 'timestamp', title: ls`Timestamp`, weight: 8},
+      {id: 'origin', title: ls`Origin`, weight: 10},
+      {id: 'swSource', title: ls`SW Source`, weight: 4},
+      {id: 'eventName', title: ls`Event`, weight: 10},
+      {id: 'instanceId', title: ls`Instance ID`, weight: 10},
     ]);
     const dataGrid = new DataGrid.DataGrid(columns);
     dataGrid.setStriped(true);
@@ -234,6 +248,22 @@
 
     this._preview.show(this._previewPanel.contentElement);
   }
+
+  /**
+   * Saves all currently displayed events in a file (JSON format).
+   */
+  async _saveToFile() {
+    const fileName = `${this._serviceName}-${new Date().toISO8601Compact()}.json`;
+    const stream = new Bindings.FileOutputStream();
+
+    const accepted = await stream.open(fileName);
+    if (!accepted)
+      return;
+
+    const events = this._model.getEvents(this._serviceName).filter(event => this._acceptEvent(event));
+    await stream.write(JSON.stringify(events, undefined, 2));
+    stream.close();
+  }
 };
 
 /**
diff --git a/third_party/blink/renderer/modules/accessibility/ax_layout_object.cc b/third_party/blink/renderer/modules/accessibility/ax_layout_object.cc
index 351c1a3..bf07487 100644
--- a/third_party/blink/renderer/modules/accessibility/ax_layout_object.cc
+++ b/third_party/blink/renderer/modules/accessibility/ax_layout_object.cc
@@ -2844,29 +2844,6 @@
   return AXObjectCache().ValidationMessageObjectIfInvalid();
 }
 
-void AXLayoutObject::LineBreaks(Vector<int>& line_breaks) const {
-  if (!IsTextControl())
-    return;
-
-  VisiblePosition visible_pos = VisiblePositionForIndex(0);
-  VisiblePosition prev_visible_pos = visible_pos;
-  visible_pos = NextLinePosition(visible_pos, LayoutUnit(), kHasEditableAXRole);
-  // nextLinePosition moves to the end of the current line when there are
-  // no more lines.
-  while (visible_pos.IsNotNull() &&
-         !InSameLine(prev_visible_pos, visible_pos)) {
-    line_breaks.push_back(IndexForVisiblePosition(visible_pos));
-    prev_visible_pos = visible_pos;
-    visible_pos =
-        NextLinePosition(visible_pos, LayoutUnit(), kHasEditableAXRole);
-
-    // Make sure we always make forward progress.
-    if (visible_pos.DeepEquivalent().CompareTo(
-            prev_visible_pos.DeepEquivalent()) < 0)
-      break;
-  }
-}
-
 // The following is a heuristic used to determine if a
 // <table> should be with ax::mojom::Role::kTable or
 // ax::mojom::Role::kLayoutTable.
diff --git a/third_party/blink/renderer/modules/accessibility/ax_layout_object.h b/third_party/blink/renderer/modules/accessibility/ax_layout_object.h
index 72a57758..c458081 100644
--- a/third_party/blink/renderer/modules/accessibility/ax_layout_object.h
+++ b/third_party/blink/renderer/modules/accessibility/ax_layout_object.h
@@ -185,7 +185,6 @@
   // Text metrics. Most of these should be deprecated, needs major cleanup.
   int Index(const VisiblePosition&) const override;
   VisiblePosition VisiblePositionForIndex(int) const override;
-  void LineBreaks(Vector<int>&) const final;
 
   // For a table.
   bool IsDataTable() const override;
diff --git a/third_party/blink/renderer/modules/accessibility/ax_object.h b/third_party/blink/renderer/modules/accessibility/ax_object.h
index 98e5689..3500a9db 100644
--- a/third_party/blink/renderer/modules/accessibility/ax_object.h
+++ b/third_party/blink/renderer/modules/accessibility/ax_object.h
@@ -996,7 +996,6 @@
   // Text metrics. Most of these should be deprecated, needs major cleanup.
   virtual VisiblePosition VisiblePositionForIndex(int) const;
   virtual int Index(const VisiblePosition&) const { return -1; }
-  virtual void LineBreaks(Vector<int>&) const {}
 
   // Static helper functions.
   static bool IsARIAControl(ax::mojom::Role);
diff --git a/third_party/blink/renderer/modules/exported/web_ax_object.cc b/third_party/blink/renderer/modules/exported/web_ax_object.cc
index f748f8d..fbdfaf7 100644
--- a/third_party/blink/renderer/modules/exported/web_ax_object.cc
+++ b/third_party/blink/renderer/modules/exported/web_ax_object.cc
@@ -1211,16 +1211,6 @@
   return private_->AccessibilityIsIgnored();
 }
 
-bool WebAXObject::LineBreaks(WebVector<int>& result) const {
-  if (IsDetached())
-    return false;
-
-  Vector<int> line_breaks_vector;
-  private_->LineBreaks(line_breaks_vector);
-  result = line_breaks_vector;
-  return true;
-}
-
 int WebAXObject::AriaColumnCount() const {
   if (IsDetached())
     return 0;
diff --git a/third_party/blink/renderer/modules/geolocation/OWNERS b/third_party/blink/renderer/modules/geolocation/OWNERS
index 51b1f3e2..2dbeb1a 100644
--- a/third_party/blink/renderer/modules/geolocation/OWNERS
+++ b/third_party/blink/renderer/modules/geolocation/OWNERS
@@ -1,5 +1,7 @@
 mattreynolds@chromium.org
+
+# Original (legacy) owner.
 mcasas@chromium.org
 
 # TEAM: device-dev@chromium.org
-# COMPONENT: Blink>Location
+# COMPONENT: Blink>Geolocation
diff --git a/third_party/blink/renderer/modules/mediacapturefromelement/OWNERS b/third_party/blink/renderer/modules/mediacapturefromelement/OWNERS
index 14affd71..49f3612 100644
--- a/third_party/blink/renderer/modules/mediacapturefromelement/OWNERS
+++ b/third_party/blink/renderer/modules/mediacapturefromelement/OWNERS
@@ -1,5 +1,7 @@
 emircan@chromium.org
+
+# Original (legacy) owner.
 mcasas@chromium.org
 
-# TEAM: media-capture-and-streams@grotations.appspotmail.com
+# TEAM: webrtc-dev@chromium.org
 # COMPONENT: Blink>MediaStream>CaptureFromElement
diff --git a/third_party/blink/renderer/modules/mediarecorder/OWNERS b/third_party/blink/renderer/modules/mediarecorder/OWNERS
index c824512..5f88414 100644
--- a/third_party/blink/renderer/modules/mediarecorder/OWNERS
+++ b/third_party/blink/renderer/modules/mediarecorder/OWNERS
@@ -1,5 +1,7 @@
-mcasas@chromium.org
 peter@chromium.org
 
+# Original (legacy) owner.
+mcasas@chromium.org
+
 # COMPONENT: Blink>MediaRecording
-# TEAM: media-dev@chromium.org
+# TEAM: webrtc-dev@chromium.org
diff --git a/third_party/blink/renderer/modules/mediastream/OWNERS b/third_party/blink/renderer/modules/mediastream/OWNERS
index fa857a6..77d16fa 100644
--- a/third_party/blink/renderer/modules/mediastream/OWNERS
+++ b/third_party/blink/renderer/modules/mediastream/OWNERS
@@ -2,5 +2,5 @@
 hbos@chromium.org
 tommi@chromium.org
 
-# TEAM: media-capture-and-streams@grotations.appspotmail.com
+# TEAM: webrtc-dev@chromium.org
 # COMPONENT: Blink>GetUserMedia
diff --git a/third_party/blink/renderer/modules/modules_initializer.cc b/third_party/blink/renderer/modules/modules_initializer.cc
index 8b29287e..014bfa4 100644
--- a/third_party/blink/renderer/modules/modules_initializer.cc
+++ b/third_party/blink/renderer/modules/modules_initializer.cc
@@ -234,7 +234,7 @@
   session->Append(MakeGarbageCollected<InspectorAccessibilityAgent>(
       inspected_frames, dom_agent));
   if (allow_view_agents) {
-    session->Append(InspectorDatabaseAgent::Create(page));
+    session->Append(MakeGarbageCollected<InspectorDatabaseAgent>(page));
     session->Append(
         MakeGarbageCollected<InspectorCacheStorageAgent>(inspected_frames));
   }
diff --git a/third_party/blink/renderer/modules/peerconnection/rtc_peer_connection.cc b/third_party/blink/renderer/modules/peerconnection/rtc_peer_connection.cc
index 9345d832..b3d4356 100644
--- a/third_party/blink/renderer/modules/peerconnection/rtc_peer_connection.cc
+++ b/third_party/blink/renderer/modules/peerconnection/rtc_peer_connection.cc
@@ -194,7 +194,7 @@
 }
 
 WebRTCOfferOptions ConvertToWebRTCOfferOptions(const RTCOfferOptions* options) {
-  return WebRTCOfferOptions(RTCOfferOptionsPlatform::Create(
+  return WebRTCOfferOptions(MakeGarbageCollected<RTCOfferOptionsPlatform>(
       options->hasOfferToReceiveVideo()
           ? std::max(options->offerToReceiveVideo(), 0)
           : -1,
@@ -208,7 +208,7 @@
 
 WebRTCAnswerOptions ConvertToWebRTCAnswerOptions(
     const RTCAnswerOptions* options) {
-  return WebRTCAnswerOptions(RTCAnswerOptionsPlatform::Create(
+  return WebRTCAnswerOptions(MakeGarbageCollected<RTCAnswerOptionsPlatform>(
       options->hasVoiceActivityDetection() ? options->voiceActivityDetection()
                                            : true));
 }
@@ -484,9 +484,10 @@
                         voice_activity_detection);
   DictionaryHelper::Get(options, "iceRestart", ice_restart);
 
-  RTCOfferOptionsPlatform* rtc_offer_options = RTCOfferOptionsPlatform::Create(
-      offer_to_receive_video, offer_to_receive_audio, voice_activity_detection,
-      ice_restart);
+  RTCOfferOptionsPlatform* rtc_offer_options =
+      MakeGarbageCollected<RTCOfferOptionsPlatform>(
+          offer_to_receive_video, offer_to_receive_audio,
+          voice_activity_detection, ice_restart);
   return rtc_offer_options;
 }
 
diff --git a/third_party/blink/renderer/modules/shapedetection/OWNERS b/third_party/blink/renderer/modules/shapedetection/OWNERS
index 2a126b8..bb37d31 100644
--- a/third_party/blink/renderer/modules/shapedetection/OWNERS
+++ b/third_party/blink/renderer/modules/shapedetection/OWNERS
@@ -1,8 +1,10 @@
-mcasas@chromium.org
 reillyg@chromium.org
 
+# Original (legacy) owner.
+mcasas@chromium.org
+
 per-file *_type_converter*.*=set noparent
 per-file *_type_converter*.*=file://ipc/SECURITY_OWNERS
 
 # COMPONENT: Blink>ImageCapture
-# TEAM: media-dev@chromium.org
+# TEAM: device-dev@chromium.org
diff --git a/third_party/blink/renderer/modules/speech/speech_synthesis_utterance.cc b/third_party/blink/renderer/modules/speech/speech_synthesis_utterance.cc
index 3ea1cd65..48866f0 100644
--- a/third_party/blink/renderer/modules/speech/speech_synthesis_utterance.cc
+++ b/third_party/blink/renderer/modules/speech/speech_synthesis_utterance.cc
@@ -26,6 +26,7 @@
 #include "third_party/blink/renderer/modules/speech/speech_synthesis_utterance.h"
 
 #include "third_party/blink/renderer/core/execution_context/execution_context.h"
+#include "third_party/blink/renderer/platform/heap/heap.h"
 
 namespace blink {
 
@@ -38,7 +39,8 @@
 SpeechSynthesisUtterance::SpeechSynthesisUtterance(ExecutionContext* context,
                                                    const String& text)
     : ContextClient(context),
-      platform_utterance_(PlatformSpeechSynthesisUtterance::Create(this)) {
+      platform_utterance_(
+          MakeGarbageCollected<PlatformSpeechSynthesisUtterance>(this)) {
   platform_utterance_->SetText(text);
 }
 
diff --git a/third_party/blink/renderer/modules/webaudio/audio_context_autoplay_test.cc b/third_party/blink/renderer/modules/webaudio/audio_context_autoplay_test.cc
index 1399f8f..0237f78 100644
--- a/third_party/blink/renderer/modules/webaudio/audio_context_autoplay_test.cc
+++ b/third_party/blink/renderer/modules/webaudio/audio_context_autoplay_test.cc
@@ -35,10 +35,6 @@
 
 class MockCrossOriginLocalFrameClient final : public EmptyLocalFrameClient {
  public:
-  static MockCrossOriginLocalFrameClient* Create(Frame* parent) {
-    return MakeGarbageCollected<MockCrossOriginLocalFrameClient>(parent);
-  }
-
   explicit MockCrossOriginLocalFrameClient(Frame* parent) : parent_(parent) {}
 
   void Trace(blink::Visitor* visitor) override {
diff --git a/third_party/blink/renderer/modules/webaudio/audio_listener.h b/third_party/blink/renderer/modules/webaudio/audio_listener.h
index 062e013..3c34cc0 100644
--- a/third_party/blink/renderer/modules/webaudio/audio_listener.h
+++ b/third_party/blink/renderer/modules/webaudio/audio_listener.h
@@ -46,11 +46,7 @@
   DEFINE_WRAPPERTYPEINFO();
 
  public:
-  static AudioListener* Create(BaseAudioContext& context) {
-    return MakeGarbageCollected<AudioListener>(context);
-  }
-
-  AudioListener(BaseAudioContext&);
+  explicit AudioListener(BaseAudioContext&);
   ~AudioListener() override;
 
   // Location of the listener
diff --git a/third_party/blink/renderer/modules/webaudio/audio_worklet.cc b/third_party/blink/renderer/modules/webaudio/audio_worklet.cc
index 004b6dc..1e51464 100644
--- a/third_party/blink/renderer/modules/webaudio/audio_worklet.cc
+++ b/third_party/blink/renderer/modules/webaudio/audio_worklet.cc
@@ -16,10 +16,6 @@
 
 namespace blink {
 
-AudioWorklet* AudioWorklet::Create(BaseAudioContext* context) {
-  return MakeGarbageCollected<AudioWorklet>(context);
-}
-
 AudioWorklet::AudioWorklet(BaseAudioContext* context)
     : Worklet(To<Document>(context->GetExecutionContext())),
       context_(context) {}
diff --git a/third_party/blink/renderer/modules/webaudio/audio_worklet.h b/third_party/blink/renderer/modules/webaudio/audio_worklet.h
index 5bf74a05..1a51696 100644
--- a/third_party/blink/renderer/modules/webaudio/audio_worklet.h
+++ b/third_party/blink/renderer/modules/webaudio/audio_worklet.h
@@ -24,8 +24,6 @@
   USING_GARBAGE_COLLECTED_MIXIN(AudioWorklet);
 
  public:
-  static AudioWorklet* Create(BaseAudioContext*);
-
   explicit AudioWorklet(BaseAudioContext*);
   ~AudioWorklet() override = default;
 
diff --git a/third_party/blink/renderer/modules/webaudio/audio_worklet_global_scope.cc b/third_party/blink/renderer/modules/webaudio/audio_worklet_global_scope.cc
index 4ccfe14..01f826fb 100644
--- a/third_party/blink/renderer/modules/webaudio/audio_worklet_global_scope.cc
+++ b/third_party/blink/renderer/modules/webaudio/audio_worklet_global_scope.cc
@@ -35,13 +35,6 @@
 
 namespace blink {
 
-AudioWorkletGlobalScope* AudioWorkletGlobalScope::Create(
-    std::unique_ptr<GlobalScopeCreationParams> creation_params,
-    WorkerThread* thread) {
-  return MakeGarbageCollected<AudioWorkletGlobalScope>(
-      std::move(creation_params), thread);
-}
-
 AudioWorkletGlobalScope::AudioWorkletGlobalScope(
     std::unique_ptr<GlobalScopeCreationParams> creation_params,
     WorkerThread* thread)
diff --git a/third_party/blink/renderer/modules/webaudio/audio_worklet_global_scope.h b/third_party/blink/renderer/modules/webaudio/audio_worklet_global_scope.h
index ed961630..afc99d1 100644
--- a/third_party/blink/renderer/modules/webaudio/audio_worklet_global_scope.h
+++ b/third_party/blink/renderer/modules/webaudio/audio_worklet_global_scope.h
@@ -53,10 +53,6 @@
   DEFINE_WRAPPERTYPEINFO();
 
  public:
-  static AudioWorkletGlobalScope* Create(
-      std::unique_ptr<GlobalScopeCreationParams>,
-      WorkerThread*);
-
   AudioWorkletGlobalScope(std::unique_ptr<GlobalScopeCreationParams>,
                           WorkerThread*);
   ~AudioWorkletGlobalScope() override;
diff --git a/third_party/blink/renderer/modules/webaudio/audio_worklet_thread.cc b/third_party/blink/renderer/modules/webaudio/audio_worklet_thread.cc
index 80f8318c..5ecf1cc 100644
--- a/third_party/blink/renderer/modules/webaudio/audio_worklet_thread.cc
+++ b/third_party/blink/renderer/modules/webaudio/audio_worklet_thread.cc
@@ -67,7 +67,8 @@
     std::unique_ptr<GlobalScopeCreationParams> creation_params) {
   TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("audio-worklet"),
                "AudioWorkletThread::createWorkerGlobalScope");
-  return AudioWorkletGlobalScope::Create(std::move(creation_params), this);
+  return MakeGarbageCollected<AudioWorkletGlobalScope>(
+      std::move(creation_params), this);
 }
 
 }  // namespace blink
diff --git a/third_party/blink/renderer/modules/webaudio/base_audio_context.cc b/third_party/blink/renderer/modules/webaudio/base_audio_context.cc
index 8c608b3..b3cd206 100644
--- a/third_party/blink/renderer/modules/webaudio/base_audio_context.cc
+++ b/third_party/blink/renderer/modules/webaudio/base_audio_context.cc
@@ -120,7 +120,7 @@
 
   FFTFrame::Initialize();
 
-  audio_worklet_ = AudioWorklet::Create(this);
+  audio_worklet_ = MakeGarbageCollected<AudioWorklet>(this);
 
   if (destination_node_) {
     destination_node_->Handler().Initialize();
@@ -134,7 +134,7 @@
 
     // The AudioParams in the listener need access to the destination node, so
     // only create the listener if the destination node exists.
-    listener_ = AudioListener::Create(*this);
+    listener_ = MakeGarbageCollected<AudioListener>(*this);
   }
 }
 
diff --git a/third_party/blink/renderer/modules/webdatabase/change_version_wrapper.h b/third_party/blink/renderer/modules/webdatabase/change_version_wrapper.h
index f7c9896..96b21f1 100644
--- a/third_party/blink/renderer/modules/webdatabase/change_version_wrapper.h
+++ b/third_party/blink/renderer/modules/webdatabase/change_version_wrapper.h
@@ -39,11 +39,6 @@
 
 class ChangeVersionWrapper final : public SQLTransactionWrapper {
  public:
-  static ChangeVersionWrapper* Create(const String& old_version,
-                                      const String& new_version) {
-    return MakeGarbageCollected<ChangeVersionWrapper>(old_version, new_version);
-  }
-
   ChangeVersionWrapper(const String& old_version, const String& new_version);
 
   bool PerformPreflight(SQLTransactionBackend*) override;
diff --git a/third_party/blink/renderer/modules/webdatabase/database.cc b/third_party/blink/renderer/modules/webdatabase/database.cc
index 47fa3aa..a477ee5 100644
--- a/third_party/blink/renderer/modules/webdatabase/database.cc
+++ b/third_party/blink/renderer/modules/webdatabase/database.cc
@@ -346,12 +346,13 @@
     return nullptr;
 
   SQLTransactionWrapper* wrapper = nullptr;
-  if (data)
-    wrapper =
-        ChangeVersionWrapper::Create(data->OldVersion(), data->NewVersion());
+  if (data) {
+    wrapper = MakeGarbageCollected<ChangeVersionWrapper>(data->OldVersion(),
+                                                         data->NewVersion());
+  }
 
-  SQLTransactionBackend* transaction_backend =
-      SQLTransactionBackend::Create(this, transaction, wrapper, read_only);
+  auto* transaction_backend = MakeGarbageCollected<SQLTransactionBackend>(
+      this, transaction, wrapper, read_only);
   transaction_queue_.push_back(transaction_backend);
   if (!transaction_in_progress_)
     ScheduleTransaction();
@@ -806,7 +807,7 @@
 static void CallTransactionErrorCallback(
     SQLTransaction::OnErrorCallback* callback,
     std::unique_ptr<SQLErrorData> error_data) {
-  callback->OnError(SQLError::Create(*error_data));
+  callback->OnError(MakeGarbageCollected<SQLError>(*error_data));
 }
 
 void Database::RunTransaction(
diff --git a/third_party/blink/renderer/modules/webdatabase/database_context.cc b/third_party/blink/renderer/modules/webdatabase/database_context.cc
index 765aaaa..a77f130 100644
--- a/third_party/blink/renderer/modules/webdatabase/database_context.cc
+++ b/third_party/blink/renderer/modules/webdatabase/database_context.cc
@@ -145,7 +145,7 @@
     // Create the database thread on first request - but not if at least one
     // database was already opened, because in that case we already had a
     // database thread and terminated it and should not create another.
-    database_thread_ = DatabaseThread::Create();
+    database_thread_ = MakeGarbageCollected<DatabaseThread>();
     database_thread_->Start();
   }
 
diff --git a/third_party/blink/renderer/modules/webdatabase/database_thread.h b/third_party/blink/renderer/modules/webdatabase/database_thread.h
index fc146e7..f3628ca 100644
--- a/third_party/blink/renderer/modules/webdatabase/database_thread.h
+++ b/third_party/blink/renderer/modules/webdatabase/database_thread.h
@@ -44,10 +44,6 @@
 
 class DatabaseThread : public GarbageCollectedFinalized<DatabaseThread> {
  public:
-  static DatabaseThread* Create() {
-    return MakeGarbageCollected<DatabaseThread>();
-  }
-
   DatabaseThread();
   ~DatabaseThread();
   void Trace(blink::Visitor*);
diff --git a/third_party/blink/renderer/modules/webdatabase/inspector_database_agent.cc b/third_party/blink/renderer/modules/webdatabase/inspector_database_agent.cc
index 4e21c51..3dc7108 100644
--- a/third_party/blink/renderer/modules/webdatabase/inspector_database_agent.cc
+++ b/third_party/blink/renderer/modules/webdatabase/inspector_database_agent.cc
@@ -82,11 +82,6 @@
 
 class StatementCallback final : public SQLStatement::OnSuccessCallback {
  public:
-  static StatementCallback* Create(
-      scoped_refptr<ExecuteSQLCallbackWrapper> request_callback) {
-    return MakeGarbageCollected<StatementCallback>(std::move(request_callback));
-  }
-
   explicit StatementCallback(
       scoped_refptr<ExecuteSQLCallbackWrapper> request_callback)
       : request_callback_(std::move(request_callback)) {}
@@ -130,12 +125,6 @@
 
 class StatementErrorCallback final : public SQLStatement::OnErrorCallback {
  public:
-  static StatementErrorCallback* Create(
-      scoped_refptr<ExecuteSQLCallbackWrapper> request_callback) {
-    return MakeGarbageCollected<StatementErrorCallback>(
-        std::move(request_callback));
-  }
-
   explicit StatementErrorCallback(
       scoped_refptr<ExecuteSQLCallbackWrapper> request_callback)
       : request_callback_(std::move(request_callback)) {}
@@ -152,13 +141,6 @@
 
 class TransactionCallback final : public SQLTransaction::OnProcessCallback {
  public:
-  static TransactionCallback* Create(
-      const String& sql_statement,
-      scoped_refptr<ExecuteSQLCallbackWrapper> request_callback) {
-    return MakeGarbageCollected<TransactionCallback>(
-        sql_statement, std::move(request_callback));
-  }
-
   explicit TransactionCallback(
       const String& sql_statement,
       scoped_refptr<ExecuteSQLCallbackWrapper> request_callback)
@@ -168,10 +150,11 @@
 
   bool OnProcess(SQLTransaction* transaction) override {
     Vector<SQLValue> sql_values;
-    transaction->ExecuteSQL(sql_statement_, sql_values,
-                            StatementCallback::Create(request_callback_),
-                            StatementErrorCallback::Create(request_callback_),
-                            IGNORE_EXCEPTION_FOR_TESTING);
+    transaction->ExecuteSQL(
+        sql_statement_, sql_values,
+        MakeGarbageCollected<StatementCallback>(request_callback_),
+        MakeGarbageCollected<StatementErrorCallback>(request_callback_),
+        IGNORE_EXCEPTION_FOR_TESTING);
     return true;
   }
 
@@ -220,8 +203,8 @@
     return;
   }
 
-  InspectorDatabaseResource* resource =
-      InspectorDatabaseResource::Create(database, domain, name, version);
+  auto* resource = MakeGarbageCollected<InspectorDatabaseResource>(
+      database, domain, name, version);
   resources_.Set(resource->Id(), resource);
   // Resources are only bound while visible.
   DCHECK(enabled_.Get());
@@ -313,7 +296,7 @@
 
   scoped_refptr<ExecuteSQLCallbackWrapper> wrapper =
       ExecuteSQLCallbackWrapper::Create(std::move(request_callback));
-  TransactionCallback* callback = TransactionCallback::Create(query, wrapper);
+  auto* callback = MakeGarbageCollected<TransactionCallback>(query, wrapper);
   TransactionErrorCallback* error_callback =
       TransactionErrorCallback::Create(wrapper);
   SQLTransaction::OnSuccessCallback* success_callback = nullptr;
diff --git a/third_party/blink/renderer/modules/webdatabase/inspector_database_agent.h b/third_party/blink/renderer/modules/webdatabase/inspector_database_agent.h
index 7d1e8c30..9b9e502 100644
--- a/third_party/blink/renderer/modules/webdatabase/inspector_database_agent.h
+++ b/third_party/blink/renderer/modules/webdatabase/inspector_database_agent.h
@@ -46,10 +46,6 @@
 class MODULES_EXPORT InspectorDatabaseAgent final
     : public InspectorBaseAgent<protocol::Database::Metainfo> {
  public:
-  static InspectorDatabaseAgent* Create(Page* page) {
-    return MakeGarbageCollected<InspectorDatabaseAgent>(page);
-  }
-
   explicit InspectorDatabaseAgent(Page*);
   ~InspectorDatabaseAgent() override;
   void Trace(blink::Visitor*) override;
diff --git a/third_party/blink/renderer/modules/webdatabase/inspector_database_resource.cc b/third_party/blink/renderer/modules/webdatabase/inspector_database_resource.cc
index 9a696e6..8926d25 100644
--- a/third_party/blink/renderer/modules/webdatabase/inspector_database_resource.cc
+++ b/third_party/blink/renderer/modules/webdatabase/inspector_database_resource.cc
@@ -36,15 +36,6 @@
 
 static int g_next_unused_id = 1;
 
-InspectorDatabaseResource* InspectorDatabaseResource::Create(
-    Database* database,
-    const String& domain,
-    const String& name,
-    const String& version) {
-  return MakeGarbageCollected<InspectorDatabaseResource>(database, domain, name,
-                                                         version);
-}
-
 InspectorDatabaseResource::InspectorDatabaseResource(Database* database,
                                                      const String& domain,
                                                      const String& name,
diff --git a/third_party/blink/renderer/modules/webdatabase/inspector_database_resource.h b/third_party/blink/renderer/modules/webdatabase/inspector_database_resource.h
index f5d59f0..dadec74 100644
--- a/third_party/blink/renderer/modules/webdatabase/inspector_database_resource.h
+++ b/third_party/blink/renderer/modules/webdatabase/inspector_database_resource.h
@@ -42,10 +42,6 @@
 class InspectorDatabaseResource
     : public GarbageCollectedFinalized<InspectorDatabaseResource> {
  public:
-  static InspectorDatabaseResource* Create(Database*,
-                                           const String& domain,
-                                           const String& name,
-                                           const String& version);
   InspectorDatabaseResource(Database*,
                             const String& domain,
                             const String& name,
diff --git a/third_party/blink/renderer/modules/webdatabase/sql_error.h b/third_party/blink/renderer/modules/webdatabase/sql_error.h
index 0cf5de3..5897d26 100644
--- a/third_party/blink/renderer/modules/webdatabase/sql_error.h
+++ b/third_party/blink/renderer/modules/webdatabase/sql_error.h
@@ -68,10 +68,6 @@
   DEFINE_WRAPPERTYPEINFO();
 
  public:
-  static SQLError* Create(const SQLErrorData& data) {
-    return MakeGarbageCollected<SQLError>(data);
-  }
-
   explicit SQLError(const SQLErrorData& data) : data_(data) {}
 
   unsigned code() const { return data_.Code(); }
diff --git a/third_party/blink/renderer/modules/webdatabase/sql_result_set.cc b/third_party/blink/renderer/modules/webdatabase/sql_result_set.cc
index 3bef9f7..b974182 100644
--- a/third_party/blink/renderer/modules/webdatabase/sql_result_set.cc
+++ b/third_party/blink/renderer/modules/webdatabase/sql_result_set.cc
@@ -34,7 +34,7 @@
 namespace blink {
 
 SQLResultSet::SQLResultSet()
-    : rows_(SQLResultSetRowList::Create()),
+    : rows_(MakeGarbageCollected<SQLResultSetRowList>()),
       insert_id_(0),
       rows_affected_(0),
       insert_id_set_(false),
diff --git a/third_party/blink/renderer/modules/webdatabase/sql_result_set.h b/third_party/blink/renderer/modules/webdatabase/sql_result_set.h
index 8577ba9..5afa63a 100644
--- a/third_party/blink/renderer/modules/webdatabase/sql_result_set.h
+++ b/third_party/blink/renderer/modules/webdatabase/sql_result_set.h
@@ -41,8 +41,6 @@
   DEFINE_WRAPPERTYPEINFO();
 
  public:
-  static SQLResultSet* Create() { return MakeGarbageCollected<SQLResultSet>(); }
-
   SQLResultSet();
 
   void Trace(blink::Visitor*) override;
diff --git a/third_party/blink/renderer/modules/webdatabase/sql_result_set_row_list.h b/third_party/blink/renderer/modules/webdatabase/sql_result_set_row_list.h
index 206ce9b0..48a7524 100644
--- a/third_party/blink/renderer/modules/webdatabase/sql_result_set_row_list.h
+++ b/third_party/blink/renderer/modules/webdatabase/sql_result_set_row_list.h
@@ -43,10 +43,6 @@
   DEFINE_WRAPPERTYPEINFO();
 
  public:
-  static SQLResultSetRowList* Create() {
-    return MakeGarbageCollected<SQLResultSetRowList>();
-  }
-
   SQLResultSetRowList() = default;
 
   const Vector<String>& ColumnNames() const { return columns_; }
diff --git a/third_party/blink/renderer/modules/webdatabase/sql_statement.cc b/third_party/blink/renderer/modules/webdatabase/sql_statement.cc
index d060881..f35c9540 100644
--- a/third_party/blink/renderer/modules/webdatabase/sql_statement.cc
+++ b/third_party/blink/renderer/modules/webdatabase/sql_statement.cc
@@ -83,12 +83,6 @@
   return return_value;
 }
 
-SQLStatement* SQLStatement::Create(Database* database,
-                                   OnSuccessCallback* callback,
-                                   OnErrorCallback* error_callback) {
-  return MakeGarbageCollected<SQLStatement>(database, callback, error_callback);
-}
-
 SQLStatement::SQLStatement(Database* database,
                            OnSuccessCallback* callback,
                            OnErrorCallback* error_callback)
@@ -136,8 +130,8 @@
   // error, because then we need to jump to the transaction error callback.
   if (error) {
     if (error_callback) {
-      callback_error =
-          error_callback->OnError(transaction, SQLError::Create(*error));
+      callback_error = error_callback->OnError(
+          transaction, MakeGarbageCollected<SQLError>(*error));
     }
   } else if (callback) {
     callback_error =
diff --git a/third_party/blink/renderer/modules/webdatabase/sql_statement_backend.cc b/third_party/blink/renderer/modules/webdatabase/sql_statement_backend.cc
index 2e9d147..4473669 100644
--- a/third_party/blink/renderer/modules/webdatabase/sql_statement_backend.cc
+++ b/third_party/blink/renderer/modules/webdatabase/sql_statement_backend.cc
@@ -82,15 +82,6 @@
 
 namespace blink {
 
-SQLStatementBackend* SQLStatementBackend::Create(
-    SQLStatement* frontend,
-    const String& statement,
-    const Vector<SQLValue>& arguments,
-    int permissions) {
-  return MakeGarbageCollected<SQLStatementBackend>(frontend, statement,
-                                                   arguments, permissions);
-}
-
 SQLStatementBackend::SQLStatementBackend(SQLStatement* frontend,
                                          const String& statement,
                                          const Vector<SQLValue>& arguments,
@@ -100,7 +91,7 @@
       arguments_(arguments),
       has_callback_(frontend_->HasCallback()),
       has_error_callback_(frontend_->HasErrorCallback()),
-      result_set_(SQLResultSet::Create()),
+      result_set_(MakeGarbageCollected<SQLResultSet>()),
       permissions_(permissions) {
   DCHECK(IsMainThread());
 
diff --git a/third_party/blink/renderer/modules/webdatabase/sql_statement_backend.h b/third_party/blink/renderer/modules/webdatabase/sql_statement_backend.h
index bd4174c5..cc1756d3 100644
--- a/third_party/blink/renderer/modules/webdatabase/sql_statement_backend.h
+++ b/third_party/blink/renderer/modules/webdatabase/sql_statement_backend.h
@@ -45,11 +45,6 @@
 class SQLStatementBackend final
     : public GarbageCollectedFinalized<SQLStatementBackend> {
  public:
-  static SQLStatementBackend* Create(SQLStatement*,
-                                     const String& sql_statement,
-                                     const Vector<SQLValue>& arguments,
-                                     int permissions);
-
   SQLStatementBackend(SQLStatement*,
                       const String& statement,
                       const Vector<SQLValue>& arguments,
diff --git a/third_party/blink/renderer/modules/webdatabase/sql_transaction.cc b/third_party/blink/renderer/modules/webdatabase/sql_transaction.cc
index 5bccafb6..ca901e6 100644
--- a/third_party/blink/renderer/modules/webdatabase/sql_transaction.cc
+++ b/third_party/blink/renderer/modules/webdatabase/sql_transaction.cc
@@ -223,7 +223,8 @@
           std::make_unique<SQLErrorData>(*backend_->TransactionError());
     }
     DCHECK(transaction_error_);
-    error_callback->OnError(SQLError::Create(*transaction_error_));
+    error_callback->OnError(
+        MakeGarbageCollected<SQLError>(*transaction_error_));
 
     transaction_error_ = nullptr;
   }
@@ -328,8 +329,8 @@
   else if (read_only_)
     permissions |= DatabaseAuthorizer::kReadOnlyMask;
 
-  SQLStatement* statement =
-      SQLStatement::Create(database_.Get(), callback, callback_error);
+  auto* statement = MakeGarbageCollected<SQLStatement>(
+      database_.Get(), callback, callback_error);
   backend_->ExecuteSQL(statement, sql_statement, arguments, permissions);
 }
 
diff --git a/third_party/blink/renderer/modules/webdatabase/sql_transaction_backend.cc b/third_party/blink/renderer/modules/webdatabase/sql_transaction_backend.cc
index 8ec97828..b71b5f5 100644
--- a/third_party/blink/renderer/modules/webdatabase/sql_transaction_backend.cc
+++ b/third_party/blink/renderer/modules/webdatabase/sql_transaction_backend.cc
@@ -366,15 +366,6 @@
 
 namespace blink {
 
-SQLTransactionBackend* SQLTransactionBackend::Create(
-    Database* db,
-    SQLTransaction* frontend,
-    SQLTransactionWrapper* wrapper,
-    bool read_only) {
-  return MakeGarbageCollected<SQLTransactionBackend>(db, frontend, wrapper,
-                                                     read_only);
-}
-
 SQLTransactionBackend::SQLTransactionBackend(Database* db,
                                              SQLTransaction* frontend,
                                              SQLTransactionWrapper* wrapper,
@@ -563,8 +554,8 @@
                                        const Vector<SQLValue>& arguments,
                                        int permissions) {
   DCHECK(IsMainThread());
-  EnqueueStatementBackend(SQLStatementBackend::Create(statement, sql_statement,
-                                                      arguments, permissions));
+  EnqueueStatementBackend(MakeGarbageCollected<SQLStatementBackend>(
+      statement, sql_statement, arguments, permissions));
 }
 
 void SQLTransactionBackend::NotifyDatabaseThreadIsShuttingDown() {
diff --git a/third_party/blink/renderer/modules/webdatabase/sql_transaction_backend.h b/third_party/blink/renderer/modules/webdatabase/sql_transaction_backend.h
index d6197d4..3525a91 100644
--- a/third_party/blink/renderer/modules/webdatabase/sql_transaction_backend.h
+++ b/third_party/blink/renderer/modules/webdatabase/sql_transaction_backend.h
@@ -62,11 +62,6 @@
     : public GarbageCollectedFinalized<SQLTransactionBackend>,
       public SQLTransactionStateMachine<SQLTransactionBackend> {
  public:
-  static SQLTransactionBackend* Create(Database*,
-                                       SQLTransaction*,
-                                       SQLTransactionWrapper*,
-                                       bool read_only);
-
   SQLTransactionBackend(Database*,
                         SQLTransaction*,
                         SQLTransactionWrapper*,
diff --git a/third_party/blink/renderer/platform/bindings/script_state.cc b/third_party/blink/renderer/platform/bindings/script_state.cc
index 67c312eb..19a54f90 100644
--- a/third_party/blink/renderer/platform/bindings/script_state.cc
+++ b/third_party/blink/renderer/platform/bindings/script_state.cc
@@ -10,11 +10,6 @@
 
 namespace blink {
 
-ScriptState* ScriptState::Create(v8::Local<v8::Context> context,
-                                 scoped_refptr<DOMWrapperWorld> world) {
-  return MakeGarbageCollected<ScriptState>(context, std::move(world));
-}
-
 ScriptState::ScriptState(v8::Local<v8::Context> context,
                          scoped_refptr<DOMWrapperWorld> world)
     : isolate_(context->GetIsolate()),
diff --git a/third_party/blink/renderer/platform/bindings/script_state.h b/third_party/blink/renderer/platform/bindings/script_state.h
index 4b93fbab..d31eaa6 100644
--- a/third_party/blink/renderer/platform/bindings/script_state.h
+++ b/third_party/blink/renderer/platform/bindings/script_state.h
@@ -12,6 +12,7 @@
 #include "third_party/blink/renderer/platform/bindings/scoped_persistent.h"
 #include "third_party/blink/renderer/platform/bindings/v8_cross_origin_setter_info.h"
 #include "third_party/blink/renderer/platform/heap/handle.h"
+#include "third_party/blink/renderer/platform/heap/heap.h"
 #include "third_party/blink/renderer/platform/heap/self_keep_alive.h"
 #include "third_party/blink/renderer/platform/platform_export.h"
 #include "v8/include/v8.h"
@@ -95,9 +96,6 @@
     v8::Local<v8::Context> context_;
   };
 
-  static ScriptState* Create(v8::Local<v8::Context>,
-                             scoped_refptr<DOMWrapperWorld>);
-
   ScriptState(v8::Local<v8::Context>, scoped_refptr<DOMWrapperWorld>);
   ~ScriptState();
 
@@ -209,10 +207,6 @@
 class ScriptStateProtectingContext
     : public GarbageCollectedFinalized<ScriptStateProtectingContext> {
  public:
-  static ScriptStateProtectingContext* Create(ScriptState* script_state) {
-    return MakeGarbageCollected<ScriptStateProtectingContext>(script_state);
-  }
-
   explicit ScriptStateProtectingContext(ScriptState* script_state)
       : script_state_(script_state) {
     if (script_state_) {
diff --git a/third_party/blink/renderer/platform/bindings/v8_per_isolate_data.cc b/third_party/blink/renderer/platform/bindings/v8_per_isolate_data.cc
index 600a10c..ac3eee8 100644
--- a/third_party/blink/renderer/platform/bindings/v8_per_isolate_data.cc
+++ b/third_party/blink/renderer/platform/bindings/v8_per_isolate_data.cc
@@ -41,6 +41,7 @@
 #include "third_party/blink/renderer/platform/bindings/v8_object_constructor.h"
 #include "third_party/blink/renderer/platform/bindings/v8_private_property.h"
 #include "third_party/blink/renderer/platform/bindings/v8_value_cache.h"
+#include "third_party/blink/renderer/platform/heap/heap.h"
 #include "third_party/blink/renderer/platform/heap/unified_heap_controller.h"
 #include "third_party/blink/renderer/platform/scheduler/public/thread.h"
 #include "third_party/blink/renderer/platform/wtf/leak_annotations.h"
@@ -290,7 +291,7 @@
   if (!script_regexp_script_state_) {
     LEAK_SANITIZER_DISABLED_SCOPE;
     v8::Local<v8::Context> context(v8::Context::New(GetIsolate()));
-    script_regexp_script_state_ = ScriptState::Create(
+    script_regexp_script_state_ = MakeGarbageCollected<ScriptState>(
         context, DOMWrapperWorld::Create(GetIsolate(),
                                          DOMWrapperWorld::WorldType::kRegExp));
   }
diff --git a/third_party/blink/renderer/platform/exported/mediastream/OWNERS b/third_party/blink/renderer/platform/exported/mediastream/OWNERS
index c205d4f9..32889cc 100644
--- a/third_party/blink/renderer/platform/exported/mediastream/OWNERS
+++ b/third_party/blink/renderer/platform/exported/mediastream/OWNERS
@@ -2,5 +2,5 @@
 
 per-file media_stream_audio_processor*=aluebs@chromium.org
 
-# TEAM: media-capture-and-streams@grotations.appspotmail.com
+# TEAM: webrtc-dev@chromium.org
 # COMPONENT: Blink>GetUserMedia
diff --git a/third_party/blink/renderer/platform/exported/web_rtc_offer_options.cc b/third_party/blink/renderer/platform/exported/web_rtc_offer_options.cc
index 6340aef..366bb3d 100644
--- a/third_party/blink/renderer/platform/exported/web_rtc_offer_options.cc
+++ b/third_party/blink/renderer/platform/exported/web_rtc_offer_options.cc
@@ -4,6 +4,7 @@
 
 #include "third_party/blink/public/platform/web_rtc_offer_options.h"
 
+#include "third_party/blink/renderer/platform/heap/heap.h"
 #include "third_party/blink/renderer/platform/peerconnection/rtc_offer_options_platform.h"
 
 namespace blink {
@@ -15,10 +16,11 @@
                                        int32_t offer_to_receive_video,
                                        bool voice_activity_detection,
                                        bool ice_restart)
-    : private_(RTCOfferOptionsPlatform::Create(offer_to_receive_audio,
-                                               offer_to_receive_video,
-                                               voice_activity_detection,
-                                               ice_restart)) {}
+    : private_(MakeGarbageCollected<RTCOfferOptionsPlatform>(
+          offer_to_receive_audio,
+          offer_to_receive_video,
+          voice_activity_detection,
+          ice_restart)) {}
 
 void WebRTCOfferOptions::Assign(const WebRTCOfferOptions& other) {
   private_ = other.private_;
diff --git a/third_party/blink/renderer/platform/graphics/canvas_2d_layer_bridge.cc b/third_party/blink/renderer/platform/graphics/canvas_2d_layer_bridge.cc
index 1c27aba..ba79034a2 100644
--- a/third_party/blink/renderer/platform/graphics/canvas_2d_layer_bridge.cc
+++ b/third_party/blink/renderer/platform/graphics/canvas_2d_layer_bridge.cc
@@ -299,6 +299,7 @@
   if (IsAccelerated() && !layer_) {
     layer_ = cc::TextureLayer::CreateForMailbox(this);
     layer_->SetIsDrawable(true);
+    layer_->SetHitTestable(true);
     layer_->SetContentsOpaque(ColorParams().GetOpacityMode() == kOpaque);
     layer_->SetBlendBackgroundColor(ColorParams().GetOpacityMode() != kOpaque);
     layer_->SetNearestNeighbor(resource_host_->FilterQuality() ==
diff --git a/third_party/blink/renderer/platform/graphics/compositing/content_layer_client_impl.cc b/third_party/blink/renderer/platform/graphics/compositing/content_layer_client_impl.cc
index 015bbac..1caf485d 100644
--- a/third_party/blink/renderer/platform/graphics/compositing/content_layer_client_impl.cc
+++ b/third_party/blink/renderer/platform/graphics/compositing/content_layer_client_impl.cc
@@ -200,6 +200,7 @@
       layer_bounds.OffsetFromOrigin());
   cc_picture_layer_->SetBounds(layer_bounds.size());
   cc_picture_layer_->SetIsDrawable(true);
+  cc_picture_layer_->SetHitTestable(true);
 
   base::Optional<RasterUnderInvalidationCheckingParams> params;
   if (RuntimeEnabledFeatures::PaintUnderInvalidationCheckingEnabled()) {
diff --git a/third_party/blink/renderer/platform/graphics/gpu/drawing_buffer.cc b/third_party/blink/renderer/platform/graphics/gpu/drawing_buffer.cc
index 4717d68..99e3da7 100644
--- a/third_party/blink/renderer/platform/graphics/gpu/drawing_buffer.cc
+++ b/third_party/blink/renderer/platform/graphics/gpu/drawing_buffer.cc
@@ -924,6 +924,7 @@
     layer_ = cc::TextureLayer::CreateForMailbox(this);
 
     layer_->SetIsDrawable(true);
+    layer_->SetHitTestable(true);
     layer_->SetContentsOpaque(!want_alpha_channel_);
     layer_->SetBlendBackgroundColor(want_alpha_channel_);
     // If premultiplied_alpha_false_texture_ exists, then premultiplied_alpha_
diff --git a/third_party/blink/renderer/platform/graphics/gpu/image_layer_bridge.cc b/third_party/blink/renderer/platform/graphics/gpu/image_layer_bridge.cc
index 1fc321b4..a0aa6435 100644
--- a/third_party/blink/renderer/platform/graphics/gpu/image_layer_bridge.cc
+++ b/third_party/blink/renderer/platform/graphics/gpu/image_layer_bridge.cc
@@ -28,6 +28,7 @@
     : opacity_mode_(opacity_mode) {
   layer_ = cc::TextureLayer::CreateForMailbox(this);
   layer_->SetIsDrawable(true);
+  layer_->SetHitTestable(true);
   layer_->SetNearestNeighbor(filter_quality_ == kNone_SkFilterQuality);
   if (opacity_mode_ == kOpaque) {
     layer_->SetContentsOpaque(true);
diff --git a/third_party/blink/renderer/platform/graphics/graphics_layer.cc b/third_party/blink/renderer/platform/graphics/graphics_layer.cc
index 7eb93baa..a0959355 100644
--- a/third_party/blink/renderer/platform/graphics/graphics_layer.cc
+++ b/third_party/blink/renderer/platform/graphics/graphics_layer.cc
@@ -75,7 +75,7 @@
       draws_content_(false),
       paints_hit_test_(false),
       contents_visible_(true),
-      hit_testable_without_draws_content_(false),
+      hit_testable_(false),
       needs_check_raster_invalidation_(false),
       has_scroll_parent_(false),
       has_clip_parent_(false),
@@ -94,6 +94,7 @@
 #endif
   layer_ = cc::PictureLayer::Create(this);
   CcLayer()->SetIsDrawable(draws_content_ && contents_visible_);
+  CcLayer()->SetHitTestable(hit_testable_);
   CcLayer()->SetLayerClient(weak_ptr_factory_.GetWeakPtr());
 
   UpdateTrackingRasterInvalidations();
@@ -544,6 +545,7 @@
   // contents_layer, for the correctness of early exit conditions in
   // SetDrawsContent() and SetContentsVisible().
   contents_layer_->SetIsDrawable(contents_visible_);
+  contents_layer_->SetHitTestable(contents_visible_);
 
   // Insert the content layer first. Video elements require this, because they
   // have shadow content that must display in front of the video.
@@ -832,11 +834,11 @@
   CcLayer()->SetIsRootForIsolatedGroup(isolated);
 }
 
-void GraphicsLayer::SetHitTestableWithoutDrawsContent(bool should_hit_test) {
-  if (hit_testable_without_draws_content_ == should_hit_test)
+void GraphicsLayer::SetHitTestable(bool should_hit_test) {
+  if (hit_testable_ == should_hit_test)
     return;
-  hit_testable_without_draws_content_ = should_hit_test;
-  CcLayer()->SetHitTestableWithoutDrawsContent(should_hit_test);
+  hit_testable_ = should_hit_test;
+  CcLayer()->SetHitTestable(should_hit_test);
 }
 
 void GraphicsLayer::SetContentsNeedsDisplay() {
diff --git a/third_party/blink/renderer/platform/graphics/graphics_layer.h b/third_party/blink/renderer/platform/graphics/graphics_layer.h
index a4b051e3..99b75a9 100644
--- a/third_party/blink/renderer/platform/graphics/graphics_layer.h
+++ b/third_party/blink/renderer/platform/graphics/graphics_layer.h
@@ -194,10 +194,8 @@
   bool IsRootForIsolatedGroup() const;
   void SetIsRootForIsolatedGroup(bool);
 
-  void SetHitTestableWithoutDrawsContent(bool);
-  bool GetHitTestableWithoutDrawsContent() const {
-    return hit_testable_without_draws_content_;
-  }
+  void SetHitTestable(bool);
+  bool GetHitTestable() const { return hit_testable_; }
 
   void SetFilters(CompositorFilterOperations);
   void SetBackdropFilters(CompositorFilterOperations, const gfx::RRectF&);
@@ -376,7 +374,7 @@
   bool draws_content_ : 1;
   bool paints_hit_test_ : 1;
   bool contents_visible_ : 1;
-  bool hit_testable_without_draws_content_ : 1;
+  bool hit_testable_ : 1;
   bool needs_check_raster_invalidation_ : 1;
 
   bool has_scroll_parent_ : 1;
diff --git a/third_party/blink/renderer/platform/graphics/surface_layer_bridge.cc b/third_party/blink/renderer/platform/graphics/surface_layer_bridge.cc
index 7955701..90975021 100644
--- a/third_party/blink/renderer/platform/graphics/surface_layer_bridge.cc
+++ b/third_party/blink/renderer/platform/graphics/surface_layer_bridge.cc
@@ -155,6 +155,7 @@
 
   surface_layer_->SetStretchContentToFillBounds(true);
   surface_layer_->SetIsDrawable(true);
+  surface_layer_->SetHitTestable(true);
   surface_layer_->SetMayContainVideo(true);
 
   if (observer_) {
diff --git a/third_party/blink/renderer/platform/heap/heap_thread_test.cc b/third_party/blink/renderer/platform/heap/heap_thread_test.cc
index 9d6facd..7eab071 100644
--- a/third_party/blink/renderer/platform/heap/heap_thread_test.cc
+++ b/third_party/blink/renderer/platform/heap/heap_thread_test.cc
@@ -7,6 +7,7 @@
 #include "third_party/blink/public/platform/platform.h"
 #include "third_party/blink/renderer/platform/cross_thread_functional.h"
 #include "third_party/blink/renderer/platform/heap/handle.h"
+#include "third_party/blink/renderer/platform/heap/heap.h"
 #include "third_party/blink/renderer/platform/heap/heap_test_utilities.h"
 #include "third_party/blink/renderer/platform/heap/thread_state.h"
 #include "third_party/blink/renderer/platform/scheduler/public/post_cross_thread_task.h"
@@ -206,10 +207,6 @@
 class DestructorLockingObject
     : public GarbageCollectedFinalized<DestructorLockingObject> {
  public:
-  static DestructorLockingObject* Create() {
-    return MakeGarbageCollected<DestructorLockingObject>();
-  }
-
   DestructorLockingObject() = default;
   virtual ~DestructorLockingObject() { ++destructor_calls_; }
 
@@ -248,7 +245,7 @@
 
   void WorkerThreadMain() override {
     // Step 2: Create an object and store the pointer.
-    object_ = DestructorLockingObject::Create();
+    object_ = MakeGarbageCollected<DestructorLockingObject>();
     SwitchToMainThread();
 
     // Step 4: Run a GC.
diff --git a/third_party/blink/renderer/platform/lifecycle_context_test.cc b/third_party/blink/renderer/platform/lifecycle_context_test.cc
index 373e4e5..aeebb725 100644
--- a/third_party/blink/renderer/platform/lifecycle_context_test.cc
+++ b/third_party/blink/renderer/platform/lifecycle_context_test.cc
@@ -26,6 +26,7 @@
 
 #include "testing/gtest/include/gtest/gtest.h"
 #include "third_party/blink/renderer/platform/heap/handle.h"
+#include "third_party/blink/renderer/platform/heap/heap.h"
 #include "third_party/blink/renderer/platform/heap/persistent.h"
 #include "third_party/blink/renderer/platform/lifecycle_notifier.h"
 #include "third_party/blink/renderer/platform/lifecycle_observer.h"
@@ -41,8 +42,6 @@
   USING_GARBAGE_COLLECTED_MIXIN(DummyContext);
 
  public:
-  static DummyContext* Create() { return MakeGarbageCollected<DummyContext>(); }
-
   void Trace(blink::Visitor* visitor) override {
     LifecycleNotifier<DummyContext, TestingObserver>::Trace(visitor);
   }
@@ -57,10 +56,6 @@
   USING_GARBAGE_COLLECTED_MIXIN(TestingObserver);
 
  public:
-  static TestingObserver* Create(DummyContext* context) {
-    return MakeGarbageCollected<TestingObserver>(context);
-  }
-
   explicit TestingObserver(DummyContext* context)
       : LifecycleObserver(context), context_destroyed_called_(false) {}
 
@@ -96,8 +91,9 @@
 };
 
 TEST(LifecycleContextTest, ShouldObserveContextDestroyed) {
-  DummyContext* context = DummyContext::Create();
-  Persistent<TestingObserver> observer = TestingObserver::Create(context);
+  auto* context = MakeGarbageCollected<DummyContext>();
+  Persistent<TestingObserver> observer =
+      MakeGarbageCollected<TestingObserver>(context);
 
   EXPECT_EQ(observer->LifecycleContext(), context);
   EXPECT_FALSE(observer->ContextDestroyedCalled());
@@ -109,8 +105,9 @@
 }
 
 TEST(LifecycleContextTest, ShouldNotObserveContextDestroyedIfUnobserve) {
-  DummyContext* context = DummyContext::Create();
-  Persistent<TestingObserver> observer = TestingObserver::Create(context);
+  auto* context = MakeGarbageCollected<DummyContext>();
+  Persistent<TestingObserver> observer =
+      MakeGarbageCollected<TestingObserver>(context);
   observer->Unobserve();
   context->NotifyContextDestroyed();
   context = nullptr;
@@ -120,9 +117,10 @@
 }
 
 TEST(LifecycleContextTest, ObserverRemovedDuringNotifyDestroyed) {
-  DummyContext* context = DummyContext::Create();
-  Persistent<TestingObserver> observer = TestingObserver::Create(context);
-  TestingObserver* inner_observer = TestingObserver::Create(context);
+  auto* context = MakeGarbageCollected<DummyContext>();
+  Persistent<TestingObserver> observer =
+      MakeGarbageCollected<TestingObserver>(context);
+  auto* inner_observer = MakeGarbageCollected<TestingObserver>(context);
   // Attach the observer to the other. When 'observer' is notified
   // of destruction, it will remove & destroy 'innerObserver'.
   observer->SetObserverToRemoveAndDestroy(inner_observer);
@@ -147,10 +145,11 @@
   ThreadState* thread_state = ThreadState::Current();
   thread_state->IncrementalMarkingStart(BlinkGC::GCReason::kForcedGCForTesting);
 
-  DummyContext* context = DummyContext::Create();
+  auto* context = MakeGarbageCollected<DummyContext>();
 
   // This should not cause a CFI check failure.
-  Persistent<TestingObserver> observer = TestingObserver::Create(context);
+  Persistent<TestingObserver> observer =
+      MakeGarbageCollected<TestingObserver>(context);
 
   EXPECT_FALSE(observer->ContextDestroyedCalled());
   context->NotifyContextDestroyed();
@@ -166,8 +165,9 @@
 }
 
 TEST(LifecycleContextTest, ForEachObserver) {
-  Persistent<DummyContext> context = DummyContext::Create();
-  Persistent<TestingObserver> observer = TestingObserver::Create(context);
+  Persistent<DummyContext> context = MakeGarbageCollected<DummyContext>();
+  Persistent<TestingObserver> observer =
+      MakeGarbageCollected<TestingObserver>(context);
 
   HeapVector<Member<TestingObserver>> seen_observers;
   context->ForEachObserver(
diff --git a/third_party/blink/renderer/platform/mhtml/archive_resource.cc b/third_party/blink/renderer/platform/mhtml/archive_resource.cc
index 2178532..71a9480 100644
--- a/third_party/blink/renderer/platform/mhtml/archive_resource.cc
+++ b/third_party/blink/renderer/platform/mhtml/archive_resource.cc
@@ -45,13 +45,4 @@
 
 ArchiveResource::~ArchiveResource() = default;
 
-ArchiveResource* ArchiveResource::Create(scoped_refptr<SharedBuffer> data,
-                                         const KURL& url,
-                                         const String& content_id,
-                                         const AtomicString& mime_type,
-                                         const AtomicString& text_encoding) {
-  return MakeGarbageCollected<ArchiveResource>(std::move(data), url, content_id,
-                                               mime_type, text_encoding);
-}
-
 }  // namespace blink
diff --git a/third_party/blink/renderer/platform/mhtml/archive_resource.h b/third_party/blink/renderer/platform/mhtml/archive_resource.h
index 7dcc5be..724bbba 100644
--- a/third_party/blink/renderer/platform/mhtml/archive_resource.h
+++ b/third_party/blink/renderer/platform/mhtml/archive_resource.h
@@ -40,12 +40,6 @@
 class PLATFORM_EXPORT ArchiveResource final
     : public GarbageCollectedFinalized<ArchiveResource> {
  public:
-  static ArchiveResource* Create(scoped_refptr<SharedBuffer>,
-                                 const KURL&,
-                                 const String& content_id,
-                                 const AtomicString& mime_type,
-                                 const AtomicString& text_encoding);
-
   ArchiveResource(scoped_refptr<SharedBuffer>,
                   const KURL&,
                   const String& content_id,
diff --git a/third_party/blink/renderer/platform/mhtml/mhtml_parser.cc b/third_party/blink/renderer/platform/mhtml/mhtml_parser.cc
index 5a411aa..b201e403 100644
--- a/third_party/blink/renderer/platform/mhtml/mhtml_parser.cc
+++ b/third_party/blink/renderer/platform/mhtml/mhtml_parser.cc
@@ -31,6 +31,8 @@
 #include "third_party/blink/renderer/platform/mhtml/mhtml_parser.h"
 
 #include <stddef.h>
+
+#include "third_party/blink/renderer/platform/heap/heap.h"
 #include "third_party/blink/renderer/platform/mhtml/archive_resource.h"
 #include "third_party/blink/renderer/platform/network/http_parsers.h"
 #include "third_party/blink/renderer/platform/network/parsed_content_type.h"
@@ -91,8 +93,6 @@
 // files.
 class MIMEHeader : public GarbageCollectedFinalized<MIMEHeader> {
  public:
-  static MIMEHeader* Create() { return MakeGarbageCollected<MIMEHeader>(); }
-
   MIMEHeader();
 
   enum class Encoding {
@@ -179,7 +179,7 @@
 }
 
 MIMEHeader* MIMEHeader::ParseHeader(SharedBufferChunkReader* buffer) {
-  MIMEHeader* mime_header = MIMEHeader::Create();
+  auto* mime_header = MakeGarbageCollected<MIMEHeader>();
   KeyValueMap key_value_pairs = RetrieveKeyValuePairs(buffer);
   KeyValueMap::iterator mime_parameters_iterator =
       key_value_pairs.find("content-type");
@@ -452,10 +452,10 @@
   // http://tools.ietf.org/html/rfc2557#section-5
   // IE and Firefox (UNMht) seem to generate only absolute URLs.
   KURL location = KURL(NullURL(), mime_header.ContentLocation());
-  return ArchiveResource::Create(content_buffer, location,
-                                 mime_header.ContentID(),
-                                 AtomicString(mime_header.ContentType()),
-                                 AtomicString(mime_header.Charset()));
+  return MakeGarbageCollected<ArchiveResource>(
+      content_buffer, location, mime_header.ContentID(),
+      AtomicString(mime_header.ContentType()),
+      AtomicString(mime_header.Charset()));
 }
 
 // static
diff --git a/third_party/blink/renderer/platform/peerconnection/rtc_answer_options_platform.h b/third_party/blink/renderer/platform/peerconnection/rtc_answer_options_platform.h
index 7de4760..6f841c8a 100644
--- a/third_party/blink/renderer/platform/peerconnection/rtc_answer_options_platform.h
+++ b/third_party/blink/renderer/platform/peerconnection/rtc_answer_options_platform.h
@@ -6,17 +6,13 @@
 #define THIRD_PARTY_BLINK_RENDERER_PLATFORM_PEERCONNECTION_RTC_ANSWER_OPTIONS_PLATFORM_H_
 
 #include "third_party/blink/renderer/platform/heap/handle.h"
+#include "third_party/blink/renderer/platform/heap/heap.h"
 
 namespace blink {
 
 class RTCAnswerOptionsPlatform final
     : public GarbageCollected<RTCAnswerOptionsPlatform> {
  public:
-  static RTCAnswerOptionsPlatform* Create(bool voice_activity_detection) {
-    return MakeGarbageCollected<RTCAnswerOptionsPlatform>(
-        voice_activity_detection);
-  }
-
   explicit RTCAnswerOptionsPlatform(bool voice_activity_detection)
       : voice_activity_detection_(voice_activity_detection) {}
 
diff --git a/third_party/blink/renderer/platform/peerconnection/rtc_offer_options_platform.h b/third_party/blink/renderer/platform/peerconnection/rtc_offer_options_platform.h
index 0ee6920..a986b96 100644
--- a/third_party/blink/renderer/platform/peerconnection/rtc_offer_options_platform.h
+++ b/third_party/blink/renderer/platform/peerconnection/rtc_offer_options_platform.h
@@ -12,15 +12,6 @@
 class RTCOfferOptionsPlatform final
     : public GarbageCollected<RTCOfferOptionsPlatform> {
  public:
-  static RTCOfferOptionsPlatform* Create(int32_t offer_to_receive_video,
-                                         int32_t offer_to_receive_audio,
-                                         bool voice_activity_detection,
-                                         bool ice_restart) {
-    return MakeGarbageCollected<RTCOfferOptionsPlatform>(
-        offer_to_receive_video, offer_to_receive_audio,
-        voice_activity_detection, ice_restart);
-  }
-
   RTCOfferOptionsPlatform(int32_t offer_to_receive_video,
                           int32_t offer_to_receive_audio,
                           bool voice_activity_detection,
diff --git a/third_party/blink/renderer/platform/prerender.h b/third_party/blink/renderer/platform/prerender.h
index 5f0f7fdd..f5cf53f 100644
--- a/third_party/blink/renderer/platform/prerender.h
+++ b/third_party/blink/renderer/platform/prerender.h
@@ -35,6 +35,7 @@
 #include "base/macros.h"
 #include "base/memory/scoped_refptr.h"
 #include "third_party/blink/renderer/platform/heap/handle.h"
+#include "third_party/blink/renderer/platform/heap/heap.h"
 #include "third_party/blink/renderer/platform/platform_export.h"
 #include "third_party/blink/renderer/platform/weborigin/kurl.h"
 #include "third_party/blink/renderer/platform/weborigin/referrer.h"
@@ -55,13 +56,6 @@
     virtual ~ExtraData() = default;
   };
 
-  static Prerender* Create(PrerenderClient* client,
-                           const KURL& url,
-                           unsigned rel_types,
-                           const Referrer& referrer) {
-    return MakeGarbageCollected<Prerender>(client, url, rel_types, referrer);
-  }
-
   Prerender(PrerenderClient*, const KURL&, unsigned rel_types, const Referrer&);
   ~Prerender();
   void Trace(blink::Visitor*);
diff --git a/third_party/blink/renderer/platform/speech/platform_speech_synthesis_utterance.cc b/third_party/blink/renderer/platform/speech/platform_speech_synthesis_utterance.cc
index defd1e33..7296694 100644
--- a/third_party/blink/renderer/platform/speech/platform_speech_synthesis_utterance.cc
+++ b/third_party/blink/renderer/platform/speech/platform_speech_synthesis_utterance.cc
@@ -27,11 +27,6 @@
 
 namespace blink {
 
-PlatformSpeechSynthesisUtterance* PlatformSpeechSynthesisUtterance::Create(
-    PlatformSpeechSynthesisUtteranceClient* client) {
-  return MakeGarbageCollected<PlatformSpeechSynthesisUtterance>(client);
-}
-
 PlatformSpeechSynthesisUtterance::PlatformSpeechSynthesisUtterance(
     PlatformSpeechSynthesisUtteranceClient* client)
     : client_(client) {}
diff --git a/third_party/blink/renderer/platform/speech/platform_speech_synthesis_utterance.h b/third_party/blink/renderer/platform/speech/platform_speech_synthesis_utterance.h
index 69ec13bf..d4d5343 100644
--- a/third_party/blink/renderer/platform/speech/platform_speech_synthesis_utterance.h
+++ b/third_party/blink/renderer/platform/speech/platform_speech_synthesis_utterance.h
@@ -45,9 +45,6 @@
 class PLATFORM_EXPORT PlatformSpeechSynthesisUtterance final
     : public GarbageCollectedFinalized<PlatformSpeechSynthesisUtterance> {
  public:
-  static PlatformSpeechSynthesisUtterance* Create(
-      PlatformSpeechSynthesisUtteranceClient*);
-
   explicit PlatformSpeechSynthesisUtterance(
       PlatformSpeechSynthesisUtteranceClient*);
 
diff --git a/third_party/blink/renderer/platform/testing/viewport_layers_setup.cc b/third_party/blink/renderer/platform/testing/viewport_layers_setup.cc
index e646556..f21a436f 100644
--- a/third_party/blink/renderer/platform/testing/viewport_layers_setup.cc
+++ b/third_party/blink/renderer/platform/testing/viewport_layers_setup.cc
@@ -22,6 +22,7 @@
   page_scale_layer_ = std::make_unique<FakeGraphicsLayer>(client_);
   graphics_layer_ = std::make_unique<FakeGraphicsLayer>(client_);
   graphics_layer_->SetDrawsContent(true);
+  graphics_layer_->SetHitTestable(true);
   clip_layer_->AddChild(scroll_elasticity_layer_.get());
   scroll_elasticity_layer_->AddChild(page_scale_layer_.get());
   page_scale_layer_->AddChild(graphics_layer_.get());
diff --git a/third_party/blink/renderer/platform/wtf/text/README.md b/third_party/blink/renderer/platform/wtf/text/README.md
new file mode 100644
index 0000000..ce8b1273
--- /dev/null
+++ b/third_party/blink/renderer/platform/wtf/text/README.md
@@ -0,0 +1,319 @@
+# Strings in Blink
+
+_Everything you always wanted to know but were afraid to ask_
+
+This document covers the `String` type in Blink, often written with an
+explicit namespace as `WTF::String` to disambiguate from string
+concepts or other types. It also briefly covers associated classes
+used for constructing strings (`StringBuilder`, `StringBuffer`), the
+internal `StringImpl` class, and the special `AtomicString` variant.
+It does not cover other text-related types or utilities (e.g.
+encodings, views, line endings, etc).
+
+## Overview
+
+A `WTF::String` represents a sequence of zero or more Unicode code
+points. A `String` can also represent one of two zero-length strings:
+the empty string and the null string. These correspond to "" and
+`null` in JavaScript, respectively. Both the empty and the null string
+return true from `String::IsEmpty()` but only the null string returns
+true from `String::IsNull()`.
+
+
+Unlike `std::string`, Blink’s `String` object is a pointer to a
+reference counted character buffer. This design makes it easier to
+share the underlying character buffer between different consumers
+because multiple consumers can reference the same underlying buffer.
+The disadvantage of this design is that we need to be careful when
+mixing Strings with multithreading because the character buffer’s
+reference counting is not thread safe.
+
+
+## Storage
+
+### Encoding
+
+A `String` can represent Unicode code points with either `LChar`s or
+`UChar`s, which use 8 bits and 16 bits per code unit respectively.
+Each `LChar` represents a single code unit of ISO-8859-1, more
+commonly called Latin-1 (hence the L in `LChar`). Unlike UTF-8, this
+encoding cannot represent every Unicode code point. However, also
+unlike UTF-8, every representable Unicode code point can represented
+with a single code unit and the code unit is simply the 8 least
+significant bits of the code point. This property makes Latin-1 an
+attractive encoding because we can decode a Latin-1 code unit to a
+Unicode code point simply by zero-extending the `LChar` value.
+
+
+Each `UChar` represents a single UTF-16 code unit (hence the U in
+`UChar`). Unfortunately, Strings do not always contain valid UTF-16
+sequences. Strings that have round-tripped through JavaScript can
+contain invalid UTF-16 sequences because JavaScript isn’t required to
+pair surrogates in its strings. Most code that works with Strings can
+ignore this issue because they operate code-unit-by-code-unit, but
+subsystems need to operate on code points outside the Basic
+Multilingual Plane need to be prepared to handle unpaired surrogates.
+
+
+In addition to `LChar` and `UChar`, Strings also use the type
+`UChar32`, which is a UTF-32 code unit. `UChar32` is particularly easy
+to work with because every UTF-32 code unit has the same numerical
+value as its corresponding Unicode code point. Practically speaking,
+that means you can treat `UChar32` values as if they were Unicode code
+points.
+
+### Layout
+
+The `String` object itself is simply a pointer to a `StringImpl`
+object, which contains the actual character buffer. `String` uses a
+`scoped_refptr<>` to automatically `AddRef()` and `Release()` the
+`StringImpl` object on construction and destruction. The `StringImpl`
+pointer can be zero, in which case the `String` represents the null
+string. Typically, `String` objects are allocated on the stack or as
+members variables. `StringImpl` objects are always allocated in the
+heap.
+
+
+`StringImpl` objects are (logically) immutable, but a given `String`
+object can refer to different `StringImpl` objects over time. For
+example, `String::Replace()` works by creating a new `StringImpl`
+object with the replaced characters rather than by mutating the
+original `StringImpl` object.
+
+
+Rather than using a fixed `sizeof(StringImpl)` allocation size,
+`StringImpl` object are allocated a variable amount of memory and
+store their character data in the same memory allocation as the
+`StringImpl` object itself. In a sense, the `StringImpl` object is a
+header for the actual array of characters, be they `LChar`s or
+`UChar`s.
+
+#### Reference count
+
+The `StringImpl` header contains three 32 bit fields. The first is a
+reference count, which is incremented and decremented by the
+`AddRef()` and `Release()` functions. When the reference count reaches
+zero the `StringImpl` object is deallocated, unless the `StringImpl`
+is marked static (in which case the `StringImpl` object is never
+deallocated).
+
+
+#### Length
+
+The next 32 bits represent the (potentially zero) length of the
+string. The length field always represents the number of code units,
+regardless of whether the `StringImpl` uses `LChar`s or `UChar`s. We
+use a 32 bit length on both 32-bit and 64-bit systems. To avoid
+creating a string whose length is too long to represent in 32 bits, we
+RELEASE_ASSERT that the length doesn’t overflow, which means we’ll
+crash in a controlled way if you try to create a string that’s
+absurdly long.
+
+#### Hash and flags
+
+The final 32 bits are used to cache the string’s hash value and to
+store a number of Boolean flags. We use 24 bits for the hash and
+reserve 8 bits for flags. As of April 2019, the flags represent
+whether the `StringImpl`:
+
+* ...contains only ASCII (7-bit), or whether this is unknown (2
+    flags).
+* ...is a member of the `AtomicString` table (discussed below).
+* ...contains `LChar`s or `UChar`s.
+* ...is Static and will never be deallocated (discussed below).
+
+We haven’t evaluated the performance trade-offs of caching the hash
+value in the `StringImpl` object recently. It’s possible that the
+cache hit rate is sufficiently low that we should remove the final 32
+bit field and move the two flags into the length, reducing the length
+field to 30 bits. Small changes to `StringImpl` can have large effects
+on the overall system, which means we should measure the performance
+impact of this sort of change carefully.
+
+
+## Construction
+
+There are multiple interfaces for constructing strings, each of which
+is useful in different situations.
+
+### `String` constructor
+The most straightforward interface for constructing strings is the
+`String` constructor. You should use this interface when the source
+data for the `String` is an array of `LChar`s or `UChar`s. The
+`String` constructor allocates a new `StringImpl` object and copies
+the source characters into the `StringImpl` object as efficiently as
+possible.
+
+
+The `String` constructor that takes a `UChar` array always creates a
+`UChar`-based `StringImpl` object even if all the source characters
+would actually fit in `LChar`s. If your source data is an array of
+`UChar`s but you have reason to believe that the string will usually
+be representable in Latin-1, you should consider using
+`StringImpl::Create8BitIfPossible()`, which creates an `LChar`- or
+`UChar`-based `StringImpl` object as appropriate at the cost of
+checking whether all the source characters can be represented in
+Latin-1.
+
+
+### `operator+`
+
+The + operator on Strings is the most efficient way to combine smaller
+strings into larger strings. Using templates, `operator+` builds a
+tree of temporary objects that mirrors the tree of `operator+`
+invocations. When the temporary objects are (implicitly) collapsed to
+a `String`, we first compute the length of the final string and then
+allocate a single `StringImpl` object of exactly the correct size.
+After allocating the object, we copy all the characters into the
+string. This approach means we copy the characters exactly once into
+the correctly sized buffer, which is maximally efficient.
+
+### `StringBuilder`
+
+If you’re unable to use `operator+` to build your string, for example
+because you need to use a loop, you should use `StringBuilder`. Like
+similar interfaces in other libraries, `StringBuilder` lets you build
+a `String` by incrementally appending content. `StringBuilder` tries
+to use 8-bit `StringImpl` objects whenever possible but will upconvert
+its internal buffer to 16 bits if necessarily.
+
+
+`StringBuilder::Append()` grows its buffer exponentially, which means
+`StringBuilder` avoids the pathologically bad O(N^2) performance that
+repeated appends/concatenation can cause. One way to further optimize
+`String` construction when using `StringBuilder` is to call
+`StringBuilder::ReserveCapacity()` with (an estimate of) the final
+length of the `String` (in code units) before appending characters. If
+you give `StringBuilder` an accurate estimate of the length of the
+string, `StringBuilder` can pre-allocate the appropriate amount of
+memory and avoid having to reallocate its buffer and copy your string.
+
+
+### `StringBuffer`
+
+Finally, there are some cases where neither the `String` constructor
+or `StringBuilder` work well. For example, sometimes rather than
+having a source array of `UChar`s from which to construct a String,
+you might have a function that will write the `UChar`s into a buffer
+you provide. `StringBuffer` can help you in these cases by allocating
+a character buffer and letting the function write into it.
+
+
+Conceptually, a `StringBuffer` represents the underlying character
+buffer from a String. However, unlike `StringImpl` objects,
+`StringBuffer`s are mutable. `StringBuffer`s work well when you know
+ahead of time exactly how large a buffer you need and whether you want
+to use `LChar`s or `UChar`s. If you’re uncertain about the length of
+the `String` you’re constructing, you probably should use
+`StringBuilder`.
+
+## Atomic Strings
+
+Some `StringImpl` objects are marked as _Atomic_, which means they’re
+stored in a thread-local `HashSet` called the `AtomicString` table.
+Rather than interacting directly with these anointed `StringImpl`
+objects, we usually hold pointers to them via `AtomicString` objects
+(rather than `String` objects). Using `AtomicString` rather than
+`String` to point to an Atomic `StringImpl` object lets the compiler
+generate (and skip!) the appropriate hash lookups in the
+`AtomicString` table as well as use faster comparison operations with
+other `AtomicString`s.
+
+### Construction
+
+Typically, constructing an `AtomicString` from a `String` object will
+involve a hash lookup in the `AtomicString` table for the current
+thread. If the string represented by the `String` object is not
+present in the `AtomicString` table, the `StringImpl` object from that
+`String` will be marked Atomic and added to the table. If the
+represented string is already present in the `AtomicString` table, the
+already-Atomic `StringImpl` object from the table will be used to
+construct the `AtomicString` rather than the `StringImpl` from the
+original String.
+
+
+If you wish to construct an `AtomicString` from an array of `LChar`s
+or `UChar`s, you should use the `AtomicString` constructor directly
+rather than first constructing a `String` object. If the string is
+already present in the `AtomicString` table, the `AtomicString`
+constructor will grab a reference to the existing Atomic `StringImpl`
+object rather than first allocating and populating a `StringImpl`
+object as the `String` constructor would.
+
+### Fast comparisons
+
+If two `StringImpl` objects are atomic, you can compare them for
+equality by comparing their addresses rather than by comparing them
+character-by-character. The reason this works is that we maintain the
+invariant that no two Atomic `StringImpl` objects on a given thread
+represent the same string. Therefore, the two `StringImpl` objects
+represent the same string if, and only if, they are actually the same
+`StringImpl` object. We’ve overloaded `operator==` on `AtomicString`
+to let the compiler generate these optimized comparisons
+automatically.
+
+### Deduplication
+
+Because there are no duplicate Atomic `StringImpl` objects on a given
+thread, `AtomicString`s are useful for coalescing duplicate strings
+into a single `StringImpl` object, saving memory. Unfortunately,
+`AtomicString`s are thread-specific and cannot be used to coalesce
+duplicate strings across threads.
+
+## Threading
+
+### Isolated copies
+
+String objects that you construct yourself are not thread-safe. The
+underlying `StringImpl` object can be shared only by `String` objects
+on the same thread because the `StringImpl`’s reference count isn’t
+incremented or decremented atomically.
+
+
+In some limited cases, you can safely send a `String` from one thread
+to another. In order for that to be safe, you need to make sure that
+the underlying `StringImpl` object has exactly one reference---the one
+you’re sending to another thread. If there is only one outstanding
+reference to the `StringImpl`, then there won’t be any reference count
+data races. The easiest way to get a `StringImpl` object with only one
+reference is to call `String::IsolatedCopy()`. You can check that a
+given `String` is safe to send to another thread by calling
+`String::IsSafeToSendToAnotherThread()`, typically in a `DCHECK`.
+
+
+If you look at the implementation of `IsSafeToSendToAnotherThread()`,
+you’ll notice that it always returns false if the `StringImpl` is
+Atomic, regardless of the reference count. That’s because Atomic
+`StringImpl` objects are not safe to send to another thread because
+they’re associated with an `AtomicString` table local to the current
+thread. If you do send an `AtomicString` to another thread and the
+`StringImpl` object is destructed on that thread, it will try to
+remove itself from that thread’s `AtomicString` table rather than from
+the original thread’s `AtomicString` table.
+
+### Static Strings
+
+At startup, we create a number of _Static_ `StringImpl` objects that
+are safe to use from any thread. These `StringImpl` objects maintain
+the invariant that the least significant bit of their reference count
+is always one, which means their reference count never reaches zero
+and they are never deallocated. In addition to preventing their
+deallocation, we also pre-populate the hash value to ensure that
+Static `StringImpl` objects are otherwise immutable.
+
+
+We first introduced these Static strings for the threaded HTML parser,
+but we are gradually using them more widely in the codebase. There are
+still some delicate interactions between Static strings and the
+`AtomicString` table, but hopefully we’ll smooth over these rough
+edges over time.
+
+# Conclusion
+
+This document contains a brief introduction to Blink’s `String` class.
+There are many details that are not included, but hopefully this
+document has given you a good high-level understanding of Strings.
+More details are available in the source, either in code or in
+comments. Happy hacking!
+
+_Originally authored by Adam Barth (abarth), 5 August 2013._
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 c437f11..efefc6c 100644
--- a/third_party/blink/renderer/platform/wtf/text/wtf_string.h
+++ b/third_party/blink/renderer/platform/wtf/text/wtf_string.h
@@ -57,8 +57,7 @@
              ? op##IgnoringASCIICase args               \
              : op##IgnoringCase args)
 
-// You can find documentation about this class in this doc:
-// https://docs.google.com/document/d/1kOCUlJdh2WJMJGDf-WoEQhmnjKLaOYRbiHz5TiGJl14/edit?usp=sharing
+// You can find documentation about this class in README.md in this directory.
 class WTF_EXPORT String {
   USING_FAST_MALLOC(String);
 
diff --git a/third_party/blink/web_tests/TestExpectations b/third_party/blink/web_tests/TestExpectations
index d8ee1f1..b9763d9 100644
--- a/third_party/blink/web_tests/TestExpectations
+++ b/third_party/blink/web_tests/TestExpectations
@@ -5114,8 +5114,10 @@
 # Prefetching Signed Exchange DevTools tests are flaky.
 crbug.com/851363 http/tests/devtools/sxg/sxg-prefetch-fail.js [ Pass Failure ]
 crbug.com/851363 virtual/nobinary-for-devtools/http/tests/devtools/sxg/sxg-prefetch-fail.js [ Pass Failure ]
+crbug.com/851363 virtual/sxg-with-network-service/http/tests/devtools/sxg/sxg-prefetch-fail.js [ Pass Failure ]
 crbug.com/851363 http/tests/devtools/sxg/sxg-prefetch.js [ Pass Failure ]
 crbug.com/851363 virtual/nobinary-for-devtools/http/tests/devtools/sxg/sxg-prefetch.js [ Pass Failure ]
+crbug.com/851363 virtual/sxg-with-network-service/http/tests/devtools/sxg/sxg-prefetch.js [ Pass Failure ]
 
 # Sheriff 2018-03-22
 crbug.com/824775 [ Win ] media/controls/video-controls-with-cast-rendering.html [ Pass Failure ]
@@ -6075,7 +6077,7 @@
 crbug.com/937811 [ Win Release ] virtual/nobinary-for-devtools/http/tests/devtools/elements/shadow/elements-panel-shadow-selection-on-refresh-2.js [ Pass Failure ]
 crbug.com/937811 [ Win Release ] http/tests/devtools/elements/shadow/elements-panel-shadow-selection-on-refresh-3.js [ Pass Failure ]
 crbug.com/937811 [ Win Release ] virtual/nobinary-for-devtools/http/tests/devtools/elements/shadow/elements-panel-shadow-selection-on-refresh-3.js [ Pass Failure ]
-crbug.com/935689 [ Mac Linux Debug ] external/wpt/css/vendor-imports/mozilla/mozilla-central-reftests/counter-styles-3/symbols-function.html [ Failure Pass ]
+crbug.com/935689 external/wpt/css/vendor-imports/mozilla/mozilla-central-reftests/counter-styles-3/symbols-function.html [ Failure Pass ]
 crbug.com/937858 [ Debug ] external/wpt/ambient-light/AmbientLightSensor.https.html [ Pass Failure ]
 crbug.com/937902 [ Linux Debug ] virtual/disable-blink-gen-property-trees/compositing/overflow/overflow-scroll-with-local-background.html [ Pass Failure ]
 crbug.com/937991 [ Win7 Release ] http/tests/devtools/cache-storage/cache-data.js [ Pass Timeout ]
diff --git a/third_party/blink/web_tests/accessibility/line-for-index-endless-loop.html b/third_party/blink/web_tests/accessibility/line-for-index-endless-loop.html
deleted file mode 100644
index e10d9ed..0000000
--- a/third_party/blink/web_tests/accessibility/line-for-index-endless-loop.html
+++ /dev/null
@@ -1,22 +0,0 @@
-<!DOCTYPE HTML>
-<script src="../resources/testharness.js"></script>
-<script src="../resources/testharnessreport.js"></script>
-<script src="../resources/run-after-layout-and-paint.js"></script>
-
-<div class="container" id="contentEditable" contentEditable>
-  <span style="display: inline-block;">
-    <span style="position: absolute;">
-      <span>x</span>
-    </span>
-    <span>2</span>
-    <span style="display: inline-block;"></span>
-  </span>
-  <span>+</span>
-</div>
-
-<script>
-test_after_layout_and_paint(function(t) {
-    var axContentEditable = accessibilityController.accessibleElementById("contentEditable");
-    assert_equals(axContentEditable.lineForIndex(0), 0);
-}, "No endless loop when getting line for index in contentEditable");
-</script>
diff --git a/third_party/blink/web_tests/accessibility/textarea-line-for-index-expected.txt b/third_party/blink/web_tests/accessibility/textarea-line-for-index-expected.txt
deleted file mode 100644
index a151a7a0..0000000
--- a/third_party/blink/web_tests/accessibility/textarea-line-for-index-expected.txt
+++ /dev/null
@@ -1,30 +0,0 @@
-This tests that the line number of a character offset is computed correctly for textarea elements.
-
-On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
-
-PASS axArea1.lineForIndex(index) is 0
-PASS axArea1.lineForIndex(index) is 0
-PASS axArea1.lineForIndex(index) is 0
-PASS axArea1.lineForIndex(index) is 0
-PASS axArea1.lineForIndex(index) is 0
-PASS axArea1.lineForIndex(index) is 0
-PASS axArea1.lineForIndex(index) is 0
-PASS axArea1.lineForIndex(index) is 1
-PASS axArea1.lineForIndex(index) is 1
-PASS axArea1.lineForIndex(index) is 1
-PASS axArea1.lineForIndex(index) is 1
-PASS axArea1.lineForIndex(index) is 1
-PASS axArea1.lineForIndex(index) is 1
-PASS axArea1.lineForIndex(index) is 1
-PASS axArea1.lineForIndex(index) is 2
-PASS axArea1.lineForIndex(index) is 2
-PASS axArea1.lineForIndex(index) is 2
-PASS axArea1.lineForIndex(index) is 2
-PASS axArea1.lineForIndex(index) is 2
-PASS axArea1.lineForIndex(index) is 2
-PASS axArea1.lineForIndex(index) is 2
-PASS axArea1.lineForIndex(area1.textLength) is 2
-PASS successfullyParsed is true
-
-TEST COMPLETE
-
diff --git a/third_party/blink/web_tests/accessibility/textarea-line-for-index.html b/third_party/blink/web_tests/accessibility/textarea-line-for-index.html
deleted file mode 100644
index d408318..0000000
--- a/third_party/blink/web_tests/accessibility/textarea-line-for-index.html
+++ /dev/null
@@ -1,35 +0,0 @@
-<!DOCTYPE html>
-<html>
-  <head>
-    <script src="../resources/js-test.js"></script>
-  </head>
-  <body>
-
-    <textarea name="area1" id="area1" rows="5" cols="40">
-line 1
-line 2
-line 3</textarea>
-
-    <script>
-        description("This tests that the line number of a character offset is computed correctly for textarea elements.");
-
-        if (window.accessibilityController) {
-
-            var area1 = document.getElementById("area1");
-            area1.focus();
-            var axArea1 = accessibilityController.focusedElement;
-
-            for (var line = 0; line < 3; ++line) {
-                for (var character = 0; character < 7; ++character) {
-                    var index = line * 7 + character;
-                    shouldBeEqualToNumber("axArea1.lineForIndex(index)", line);
-                }
-            }
-
-            // Placing the caret after the last character should not change the line.
-            shouldBeEqualToNumber("axArea1.lineForIndex(area1.textLength)", 2);
-
-        }
-    </script>
-  </body>
-</html>
diff --git a/third_party/blink/web_tests/external/wpt/geolocation-API/OWNERS b/third_party/blink/web_tests/external/wpt/geolocation-API/OWNERS
index 6d678d8..61e5876 100644
--- a/third_party/blink/web_tests/external/wpt/geolocation-API/OWNERS
+++ b/third_party/blink/web_tests/external/wpt/geolocation-API/OWNERS
@@ -1,3 +1,5 @@
 # TEAM: device-dev@chromium.org
-# COMPONENT: Blink>Location
+# COMPONENT: Blink>Geolocation
+
+# Original (legacy) owner.
 mcasas@chromium.org
diff --git a/third_party/blink/web_tests/external/wpt/html-media-capture/OWNERS b/third_party/blink/web_tests/external/wpt/html-media-capture/OWNERS
index 5db6e74..d96b9bf 100644
--- a/third_party/blink/web_tests/external/wpt/html-media-capture/OWNERS
+++ b/third_party/blink/web_tests/external/wpt/html-media-capture/OWNERS
@@ -1,4 +1,4 @@
 # TEAM: media-dev@chromium.org
 # COMPONENT: Blink>ImageCapture
+
 rijubrata.bhaumik@intel.com
-mcasas@chromium.org
diff --git a/third_party/blink/web_tests/external/wpt/mediacapture-record/OWNERS b/third_party/blink/web_tests/external/wpt/mediacapture-record/OWNERS
index 681555b..9c7779b 100644
--- a/third_party/blink/web_tests/external/wpt/mediacapture-record/OWNERS
+++ b/third_party/blink/web_tests/external/wpt/mediacapture-record/OWNERS
@@ -1,3 +1,5 @@
-# TEAM: media-dev@chromium.org
+# TEAM: webrtc-dev@chromium.org
 # COMPONENT: Blink>MediaRecording
+
+# Original (legacy) owner.
 mcasas@chromium.org
diff --git a/third_party/libwebm/OWNERS b/third_party/libwebm/OWNERS
index 43542744..ca0fd18a 100644
--- a/third_party/libwebm/OWNERS
+++ b/third_party/libwebm/OWNERS
@@ -1,9 +1,12 @@
 # The following OWNERS refer to libwebm Chromium integration.
 emircan@chromium.org
-mcasas@chromium.org
 niklase@chromium.org
 
 # The following OWNER refer to libwebm content.
 tomfinegan@chromium.org
 
+# Original (legacy) owner.
+mcasas@chromium.org
+
+# TEAM: webrtc-dev@chromium.org
 # COMPONENT: Blink>MediaRecording
diff --git a/third_party/webrtc_overrides/BUILD.gn b/third_party/webrtc_overrides/BUILD.gn
index c0de0b91..61048ce8 100644
--- a/third_party/webrtc_overrides/BUILD.gn
+++ b/third_party/webrtc_overrides/BUILD.gn
@@ -94,7 +94,6 @@
     ":webrtc",
     "//third_party/webrtc/media:rtc_media",
     "//third_party/webrtc/media:rtc_media_base",
-    "//third_party/webrtc/modules/video_capture",
     "//third_party/webrtc/pc:libjingle_peerconnection",
     "//third_party/webrtc/pc:rtc_pc",
     "//third_party/webrtc/system_wrappers",
diff --git a/tools/json_schema_compiler/feature_compiler.py b/tools/json_schema_compiler/feature_compiler.py
index 34ccc31f..d08b7a6 100644
--- a/tools/json_schema_compiler/feature_compiler.py
+++ b/tools/json_schema_compiler/feature_compiler.py
@@ -64,11 +64,10 @@
 }  // namespace extensions
 """
 
-# Returns true if the list 'l' does not contain any strings that look like
-# extension ids.
-def ListDoesNotContainPlainExtensionIds(l):
-  # For now, let's just say anything 32 characters in length is an id.
-  return len(filter(lambda s: len(s) == 32, l)) == 0
+# Returns true if the list 'l' only contains strings that are a hex-encoded SHA1
+# hashes.
+def ListContainsOnlySha1Hashes(l):
+  return len(filter(lambda s: not re.match("^[A-F0-9]{40}$", s), l)) == 0
 
 # A "grammar" for what is and isn't allowed in the features.json files. This
 # grammar has to list all possible keys and the requirements for each. The
@@ -96,6 +95,8 @@
 #                assign the list of Feature::BLESSED_EXTENSION_CONTEXT,
 #                Feature::BLESSED_WEB_PAGE_CONTEXT et al for contexts. If not
 #                specified, defaults to false.
+#   'allow_empty': Only applicable for lists. Whether an empty list is a valid
+#                  value. If omitted, empty lists are prohibited.
 #   'validators': A list of (function, str) pairs with a function to run on the
 #                 value for a feature. Validators allow for more flexible or
 #                 one-off style validation than just what's in the grammar (such
@@ -120,7 +121,7 @@
       list: {
         'subtype': unicode,
         'validators': [
-          (ListDoesNotContainPlainExtensionIds,
+          (ListContainsOnlySha1Hashes,
            'list should only have hex-encoded SHA1 hashes of extension ids')
         ]
       }
@@ -161,7 +162,12 @@
       bool: {'values': [True]}
     },
     'dependencies': {
-      list: {'subtype': unicode}
+      list: {
+        # We allow an empty list of dependencies for child features that want
+        # to override their parents' dependency set.
+        'allow_empty': True,
+        'subtype': unicode
+      }
     },
     'extension_types': {
       list: {
@@ -227,7 +233,7 @@
       list: {
         'subtype': unicode,
         'validators': [
-          (ListDoesNotContainPlainExtensionIds,
+          (ListContainsOnlySha1Hashes,
            'list should only have hex-encoded SHA1 hashes of extension ids')
         ]
       }
@@ -460,6 +466,7 @@
 
     is_all = False
     if v == 'all' and list in grammar and 'allow_all' in grammar[list]:
+      assert grammar[list]['allow_all'], '`allow_all` only supports `True`.'
       v = []
       is_all = True
 
@@ -472,6 +479,14 @@
       self._AddKeyError(key, 'Illegal value: "%s"' % v)
       return
 
+    if value_type is list and not is_all and len(v) == 0:
+      if 'allow_empty' in grammar[list]:
+        assert grammar[list]['allow_empty'], \
+               '`allow_empty` only supports `True`.'
+      else:
+        self._AddKeyError(key, 'List must specify at least one element.')
+        return
+
     expected = grammar[value_type]
     expected_values = None
     enum_map = None
diff --git a/tools/json_schema_compiler/feature_compiler_test.py b/tools/json_schema_compiler/feature_compiler_test.py
index c9c58f6..3a1f500 100755
--- a/tools/json_schema_compiler/feature_compiler_test.py
+++ b/tools/json_schema_compiler/feature_compiler_test.py
@@ -38,7 +38,10 @@
   def testFeature(self):
     # Test some basic feature parsing for a sanity check.
     f = self._parseFeature({
-      'blacklist': ['aaa', 'bbb'],
+      'blacklist': [
+        'ABCDEF0123456789ABCDEF0123456789ABCDEF01',
+        '10FEDCBA9876543210FEDCBA9876543210FEDCBA'
+      ],
       'channel': 'stable',
       'command_line_switch': 'switch',
       'component_extensions_auto_granted': False,
@@ -57,7 +60,10 @@
       'noparent': True,
       'platforms': ['mac', 'win'],
       'session_types': ['kiosk', 'regular'],
-      'whitelist': ['zzz', 'yyy']
+      'whitelist': [
+        '0123456789ABCDEF0123456789ABCDEF01234567',
+        '76543210FEDCBA9876543210FEDCBA9876543210'
+      ]
     })
     self.assertFalse(f.GetErrors())
 
@@ -95,6 +101,15 @@
     f = self._parseFeature({'noparent': False})
     self._hasError(f, 'Illegal value: "False"')
 
+  def testEmptyList(self):
+    f = self._parseFeature({'contexts': []})
+    self._hasError(f, 'List must specify at least one element.')
+
+  def testEmptyListWithAllowEmpty(self):
+    # `dependencies` is the only key that allows an empty list.
+    f = self._parseFeature({'dependencies': []})
+    self.assertFalse(f.GetErrors())
+
   def testApiFeaturesNeedContexts(self):
     f = self._parseFeature({'dependencies': 'alpha',
                             'extension_types': ['extension'],
diff --git a/tools/json_schema_compiler/test/features_generation_unittest.cc b/tools/json_schema_compiler/test/features_generation_unittest.cc
index 2335097b..32dfaad 100644
--- a/tools/json_schema_compiler/test/features_generation_unittest.cc
+++ b/tools/json_schema_compiler/test/features_generation_unittest.cc
@@ -125,8 +125,10 @@
     comparator.extension_types = {Manifest::TYPE_EXTENSION,
                                   Manifest::TYPE_PLATFORM_APP};
     comparator.location = SimpleFeature::COMPONENT_LOCATION;
-    comparator.allowlist = {"aaa", "bbb"};
-    comparator.blocklist = {"zzz", "yyy"};
+    comparator.allowlist = {"ABCDEF0123456789ABCDEF0123456789ABCDEF01",
+                            "10FEDCBA9876543210FEDCBA9876543210FEDCBA"};
+    comparator.blocklist = {"0123456789ABCDEF0123456789ABCDEF01234567",
+                            "76543210FEDCBA9876543210FEDCBA9876543210"};
     comparator.component_extensions_auto_granted = false;
     comparator.CompareFeature(feature);
   }
@@ -145,7 +147,7 @@
     // case that it specifies its own value. Thus, we reuse |comparator|.
     feature = GetAsSimpleFeature("gamma.child");
     comparator.name = "gamma.child";
-    comparator.allowlist = {"ccc"};
+    comparator.allowlist = {"0123456789ABCDEF0123456789ABCDEF01234567"};
     comparator.platforms = {Feature::LINUX_PLATFORM};
     comparator.dependencies.clear();
     comparator.CompareFeature(feature);
@@ -155,7 +157,7 @@
     // other feature.
     const SimpleFeature* feature = GetAsSimpleFeature("gamma.unparented");
     FeatureComparator comparator("gamma.unparented");
-    comparator.blocklist = {"ddd"};
+    comparator.blocklist = {"0123456789ABCDEF0123456789ABCDEF01234567"};
     comparator.contexts = {Feature::UNBLESSED_EXTENSION_CONTEXT};
     comparator.channel = version_info::Channel::DEV;
     comparator.CompareFeature(feature);
@@ -259,7 +261,7 @@
       comparator.channel = version_info::Channel::BETA;
       comparator.contexts = {Feature::BLESSED_EXTENSION_CONTEXT};
       comparator.extension_types = {Manifest::TYPE_EXTENSION};
-      comparator.allowlist = {"aaa"};
+      comparator.allowlist = {"0123456789ABCDEF0123456789ABCDEF01234567"};
       comparator.CompareFeature(other_parent);
     }
   }
diff --git a/tools/json_schema_compiler/test/features_test.json b/tools/json_schema_compiler/test/features_test.json
index 900e9ecc9..5cd6155 100644
--- a/tools/json_schema_compiler/test/features_test.json
+++ b/tools/json_schema_compiler/test/features_test.json
@@ -13,8 +13,14 @@
     "contexts": ["blessed_extension"],
     "extension_types": ["extension", "platform_app"],
     "location": "component",
-    "whitelist": ["aaa", "bbb"],
-    "blacklist": ["zzz", "yyy"],
+    "whitelist": [
+      "ABCDEF0123456789ABCDEF0123456789ABCDEF01",
+      "10FEDCBA9876543210FEDCBA9876543210FEDCBA"
+    ],
+    "blacklist": [
+      "0123456789ABCDEF0123456789ABCDEF01234567",
+      "76543210FEDCBA9876543210FEDCBA9876543210"
+    ],
     "component_extensions_auto_granted": false
   },
   "gamma": {
@@ -26,13 +32,13 @@
     "internal": true
   },
   "gamma.child": {
-    "whitelist": ["ccc"],
+    "whitelist": ["0123456789ABCDEF0123456789ABCDEF01234567"],
     "dependencies": [],
     "platforms": ["linux"]
   },
   "gamma.unparented": {
     "channel": "dev",
-    "blacklist": ["ddd"],
+    "blacklist": ["0123456789ABCDEF0123456789ABCDEF01234567"],
     "contexts": ["unblessed_extension"],
     "noparent": true
   },
@@ -55,7 +61,7 @@
     "channel": "beta",
     "contexts": ["blessed_extension"],
     "extension_types": ["extension"],
-    "whitelist": ["aaa"]
+    "whitelist": ["0123456789ABCDEF0123456789ABCDEF01234567"]
   }, {
     "channel": "stable",
     "contexts": ["blessed_extension"],
diff --git a/tools/mb/mb_config.pyl b/tools/mb/mb_config.pyl
index 67880387..496c60d 100644
--- a/tools/mb/mb_config.pyl
+++ b/tools/mb/mb_config.pyl
@@ -669,10 +669,12 @@
       'android_angle_deqp_rel_ng': 'deqp_android_release_trybot_arm64',
       'android_angle_vk32_deqp_rel_ng': 'deqp_android_vulkan_release_trybot',
       'android_angle_vk64_deqp_rel_ng': 'deqp_android_vulkan_release_trybot_arm64',
+      'linux-angle-rel': 'gpu_fyi_tests_release_trybot',
       'linux_angle_ozone_rel_ng': 'gpu_fyi_tests_ozone_linux_system_gbm_libdrm_release_trybot',
       'linux_angle_dbg_ng': 'gpu_fyi_tests_debug_trybot',
       'linux_angle_deqp_rel_ng': 'deqp_release_trybot',
       'linux_angle_rel_ng': 'gpu_fyi_tests_release_trybot',
+      'mac-angle-rel': 'gpu_fyi_tests_release_trybot',
       'mac_angle_dbg_ng': 'gpu_fyi_tests_debug_trybot',
       'mac_angle_rel_ng': 'gpu_fyi_tests_release_trybot',
       'win-angle-rel': 'gpu_fyi_tests_release_trybot_x86',
diff --git a/tools/metrics/BUILD.gn b/tools/metrics/BUILD.gn
index 25a8f5b..6f01fdd 100644
--- a/tools/metrics/BUILD.gn
+++ b/tools/metrics/BUILD.gn
@@ -97,6 +97,7 @@
     "//tools/metrics/common/path_util.py",
     "//tools/metrics/common/presubmit_util.py",
     "//tools/metrics/common/pretty_print_xml.py",
+    "//tools/metrics/common/etree_util.py",
 
     "//tools/metrics/histograms/extract_histograms.py",
     "//tools/metrics/histograms/generate_expired_histograms_array.py",
diff --git a/tools/metrics/actions/actions_print_style.py b/tools/metrics/actions/actions_print_style.py
index ed19f6a9..8d1278e 100644
--- a/tools/metrics/actions/actions_print_style.py
+++ b/tools/metrics/actions/actions_print_style.py
@@ -50,7 +50,7 @@
 # Tags that we allow to be squished into a single line for brevity.
 TAGS_THAT_ALLOW_SINGLE_LINE = ['obsolete', 'owner', 'description']
 
-LOWERCASE_NAME_FN = lambda n: n.attributes['name'].value.lower()
+LOWERCASE_NAME_FN = lambda n: n.get('name').lower()
 
 # Tags whose children we want to alphabetize. The key is the parent tag name,
 # and the value is a list of pairs of tag name and key functions that maps each
diff --git a/tools/metrics/common/etree_util.py b/tools/metrics/common/etree_util.py
new file mode 100644
index 0000000..cbfcbe8
--- /dev/null
+++ b/tools/metrics/common/etree_util.py
@@ -0,0 +1,94 @@
+# 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.
+
+"""Utility functions for parsing XML strings into ElementTree nodes."""
+
+import xml.etree.ElementTree as ET
+import xml.sax
+
+
+class _FirstTagFoundError(Exception):
+  """Raised when the first tag is found in an XML document.
+
+  This isn't actually an error. Raising this exception is how we end parsing XML
+  documents early.
+  """
+  pass
+
+
+class _FirstTagFinder(xml.sax.ContentHandler):
+  """An XML SAX parser that raises as soon as a tag is found.
+
+  Call getFirstTagLine to determine which line the tag was found on.
+  """
+
+  def __init__(self):
+    xml.sax.ContentHandler.__init__(self)
+    self.first_tag_line = 0
+    self.first_tag_column = 0
+
+  def GetFirstTagLine(self):
+    return self.first_tag_line
+
+  def GetFirstTagColumn(self):
+    return self.first_tag_column
+
+  def setDocumentLocator(self, locator):
+    self.location = locator
+
+  def startElement(self, tag, attributes):
+    del tag, attributes  # Unused.
+
+    # Now that the first tag is found, remember the location of it.
+    self.first_tag_line = self.location.getLineNumber()
+    self.first_tag_column = self.location.getColumnNumber()
+
+    # End parsing by throwing.
+    raise _FirstTagFoundError()
+
+
+class _CommentedXMLParser(ET.XMLParser):
+  """An ElementTree builder that preserves comments."""
+
+  def __init__(self, *args, **kwargs):
+    super(_CommentedXMLParser, self).__init__(*args, **kwargs)
+    self._parser.CommentHandler = self.comment
+
+  def comment(self, data):  # pylint: disable=invalid-name
+    self._target.start(ET.Comment, {})
+    self._target.data(data)
+    self._target.end(ET.Comment)
+
+
+def GetTopLevelContent(file_content):
+  """Returns a string of all the text in the xml file before the first tag."""
+  handler = _FirstTagFinder()
+
+  first_tag_line = 0
+  first_tag_column = 0
+  try:
+    xml.sax.parseString(file_content, handler)
+  except _FirstTagFoundError:
+    # This is the expected case, it means a tag was found in the doc.
+    first_tag_line = handler.GetFirstTagLine()
+    first_tag_column = handler.GetFirstTagColumn()
+  if first_tag_line == 0 and first_tag_column == 0:
+    return ''
+
+  char = 0
+  for _ in range(first_tag_line - 1):
+    char = file_content.index('\n', char) + 1
+  char += first_tag_column - 1
+
+  # |char| is now pointing at the final character before the opening tag '<'.
+  top_content = file_content[:char + 1].strip()
+  if not top_content:
+    return ''
+
+  return top_content + '\n\n'
+
+
+def ParseXMLString(raw_xml):
+  """Parses raw_xml and returns an ElementTree node that includes comments."""
+  return ET.fromstring(raw_xml, _CommentedXMLParser())
diff --git a/tools/metrics/common/pretty_print_xml.py b/tools/metrics/common/pretty_print_xml.py
index ae97ff7..15ab216 100644
--- a/tools/metrics/common/pretty_print_xml.py
+++ b/tools/metrics/common/pretty_print_xml.py
@@ -11,7 +11,10 @@
 import sys
 import logging
 import textwrap
-import xml.dom.minidom
+from xml.dom import minidom
+import xml.etree.ElementTree as ET
+
+import etree_util
 
 WRAP_COLUMN = 80
 
@@ -29,7 +32,8 @@
   Returns:
     The length of the last line in s, in characters.
   """
-  if s.rfind('\n') == -1: return len(s)
+  if s.rfind('\n') == -1:
+    return len(s)
   return len(s) - s.rfind('\n') - len('\n')
 
 
@@ -45,6 +49,7 @@
 
   Args:
     text: The text to split.
+
   Returns:
     A list of paragraphs as strings.
   """
@@ -84,58 +89,57 @@
     self.wrapper.width = WRAP_COLUMN
 
   def PrettyPrintXml(self, tree):
+    # If it's not an ElementTree instance, we assume it's minidom.
+    if not isinstance(tree, ET.Element):
+      assert isinstance(tree, minidom.Document)
+      return self._PrettyPrintMinidom(tree)
+
     tree = self._TransformByAlphabetizing(tree)
-    tree = self.PrettyPrintNode(tree)
+    tree = self.PrettyPrintElementTreeNode(tree)
     return tree
 
-  def _UnsafeAppendChild(self, parent, child):
-    """Append child to parent's list of children.
+  def _PrettyPrintMinidom(self, doc):
+    """Transforms minidom to ElementTree before pretty printing it."""
+    raw_xml = doc.toxml()
 
-    It ignores the possibility that the child is already in another node's
-    childNodes list.  Requires that the previous parent of child is discarded
-    (to avoid non-tree DOM graphs). This can provide a significant speedup as
-    O(n^2) operations are removed (in particular, each child insertion avoids
-    the need to traverse the old parent's entire list of children).
+    # minidom prepends a document type, so remove it.
+    raw_xml = raw_xml.replace(minidom.Document().toxml(), '')
 
-    Args:
-      parent: the parent node to be appended to.
-      child: the child node to append to |parent| node.
-    """
-    child.parentNode = None
-    parent.appendChild(child)
-    child.parentNode = parent
+    etree_root = etree_util.ParseXMLString(raw_xml)
+    top_content = etree_util.GetTopLevelContent(raw_xml)
+
+    # Add newlines between top-level comments.
+    top_content = top_content.replace('--><!--', '-->\n\n<!--')
+
+    formatted_xml = self.PrettyPrintXml(etree_root)
+    return top_content + formatted_xml
+
 
   def _TransformByAlphabetizing(self, node):
     """Transform the given XML by alphabetizing nodes.
 
     Args:
-      node: The minidom node to transform.
+      node: The elementtree node to transform.
 
     Returns:
-      The minidom node, with children appropriately alphabetized. Note that the
-      transformation is done in-place, i.e. the original minidom tree is
-      modified directly.
+      The elementtree node, with children appropriately alphabetized. Note that
+      the transformation is done in-place, i.e. the original tree is modified
+      directly.
     """
-    if node.nodeType != xml.dom.minidom.Node.ELEMENT_NODE:
-      for c in node.childNodes:
-        self._TransformByAlphabetizing(c)
-      return node
-
     # Element node with a tag name that we alphabetize the children of?
     alpha_rules = self.tags_alphabetization_rules
-    if node.tagName in alpha_rules:
+    if node.tag in alpha_rules:
       # Put subnodes in a list of node, key pairs to allow for custom sorting.
       subtags = {}
-      for index, (subtag, key_function) in enumerate(alpha_rules[node.tagName]):
+      for index, (subtag, key_function) in enumerate(alpha_rules[node.tag]):
         subtags[subtag] = (index, key_function)
 
       subnodes = []
       sort_key = -1
       pending_node_indices = []
-      for c in node.childNodes:
-        if (c.nodeType == xml.dom.minidom.Node.ELEMENT_NODE and
-            c.tagName in subtags):
-          subtag_sort_index, key_function = subtags[c.tagName]
+      for c in node:
+        if c.tag in subtags:
+          subtag_sort_index, key_function = subtags[c.tag]
           sort_key = (subtag_sort_index, key_function(c))
           # Replace sort keys for delayed nodes.
           for idx in pending_node_indices:
@@ -151,60 +155,66 @@
       # Sort the subnode list.
       subnodes.sort(key=lambda pair: pair[1])
 
-      # Re-add the subnodes, transforming each recursively.
-      while node.firstChild:
-        node.removeChild(node.firstChild)
+      # Remove the existing nodes
+      for child in list(node):
+        node.remove(child)
+
+      # Re-add the sorted subnodes, transforming each recursively.
       for (c, _) in subnodes:
-        self._UnsafeAppendChild(node, self._TransformByAlphabetizing(c))
+        node.append(self._TransformByAlphabetizing(c))
       return node
 
     # Recursively handle other element nodes and other node types.
-    for c in node.childNodes:
+    for c in node:
       self._TransformByAlphabetizing(c)
     return node
 
-  def _PrettyPrintText(self, node, indent):
-    # Wrap each paragraph in the text to fit in the 80 column limit.
+  def _PrettyPrintText(self, text, indent):
+    """Pretty print an element."""
+    if not text.strip():
+      return ""
+
     self.wrapper.initial_indent = ' ' * indent
     self.wrapper.subsequent_indent = ' ' * indent
-    text = XmlEscape(node.data)
-    paragraphs = SplitParagraphs(text)
+    escaped_text = XmlEscape(text)
+    paragraphs = SplitParagraphs(escaped_text)
+
     # Wrap each paragraph and separate with two newlines.
     return '\n\n'.join(self.wrapper.fill(p) for p in paragraphs)
 
   def _PrettyPrintElement(self, node, indent):
     # Check if tag name is valid.
-    if node.tagName not in self.attribute_order:
-      logging.error('Unrecognized tag "%s"', node.tagName)
-      raise Error('Unrecognized tag "%s"', node.tagName)
+    if node.tag not in self.attribute_order:
+      logging.error('Unrecognized tag "%s"', node.tag)
+      raise Error('Unrecognized tag "%s"' % node.tag)
 
     # Newlines.
     newlines_after_open, newlines_before_close, newlines_after_close = (
-        self.tags_that_have_extra_newline.get(node.tagName, (1, 1, 0)))
+        self.tags_that_have_extra_newline.get(node.tag, (1, 1, 0)))
     # Open the tag.
-    s = ' ' * indent + '<' + node.tagName
+    s = ' ' * indent + '<' + node.tag
 
     # Calculate how much space to allow for the '>' or '/>'.
-    closing_chars = 1
-    if not node.childNodes:
-      closing_chars = 2
+    closing_chars = 2
+    if len(node) or node.text:
+      closing_chars = 1
 
-    attributes = node.attributes.keys()
+    attributes = node.keys()
     required_attributes = [attribute for attribute in self.required_attributes
-                           if attribute in self.attribute_order[node.tagName]]
+                           if attribute in self.attribute_order[node.tag]]
     missing_attributes = [attribute for attribute in required_attributes
                           if attribute not in attributes]
 
     for attribute in missing_attributes:
       logging.error(
-          'Missing attribute "%s" in tag "%s"', attribute, node.tagName)
+          'Missing attribute "%s" in tag "%s"', attribute, node.tag)
     if missing_attributes:
       missing_attributes_str = (
           ', '.join('"%s"' % attribute for attribute in missing_attributes))
       present_attributes = [
           ' {0}="{1}"'.format(name, value)
-          for name, value in node.attributes.items()]
-      node_str = '<{0}{1}>'.format(node.tagName, ''.join(present_attributes))
+          for name, value in node.items()]
+      node_str = '<{0}{1}>'.format(node.tag, ''.join(present_attributes))
       raise Error(
           'Missing attributes {0} in tag "{1}"'.format(
               missing_attributes_str, node_str))
@@ -212,23 +222,22 @@
     # Pretty-print the attributes.
     if attributes:
       # Reorder the attributes.
-      unrecognized_attributes = (
-          [a for a in attributes
-           if a not in self.attribute_order[node.tagName]])
-      attributes = [a for a in self.attribute_order[node.tagName]
-                    if a in attributes]
+      unrecognized_attributes = [
+          a for a in attributes if a not in self.attribute_order[node.tag]
+      ]
+      attributes = [
+          a for a in self.attribute_order[node.tag] if a in attributes
+      ]
 
       for a in unrecognized_attributes:
-        logging.error(
-            'Unrecognized attribute "%s" in tag "%s"', a, node.tagName)
+        logging.error('Unrecognized attribute "%s" in tag "%s"', a, node.tag)
       if unrecognized_attributes:
-        raise Error(
-            'Unrecognized attributes {0} in tag "{1}"'.format(
-                ', '.join('"{0}"'.format(a) for a in unrecognized_attributes),
-                node.tagName))
+        raise Error('Unrecognized attributes {0} in tag "{1}"'.format(
+            ', '.join('"{0}"'.format(a) for a in unrecognized_attributes),
+            node.tag))
 
       for a in attributes:
-        value = XmlEscape(node.attributes[a].value)
+        value = XmlEscape(node.get(a))
         # Replace sequences of whitespace with single spaces.
         words = value.split()
         a_str = ' %s="%s"' % (a, ' '.join(words))
@@ -255,25 +264,39 @@
       s = s.rstrip()  # remove any trailing whitespace
 
     # Pretty-print the child nodes.
-    if node.childNodes:
+    if len(node) > 0 or node.text:  # pylint: disable=g-explicit-length-test
       s += '>'
       # Calculate the new indent level for child nodes.
       new_indent = indent
-      if node.tagName not in self.tags_that_dont_indent:
+      if node.tag not in self.tags_that_dont_indent:
         new_indent += 2
-      child_nodes = node.childNodes
+
+      children = []
+      for c in node:
+        children.append(c)
 
       # Recursively pretty-print the child nodes.
-      child_nodes = [self.PrettyPrintNode(n, indent=new_indent)
-                     for n in child_nodes]
-      child_nodes = [c for c in child_nodes if c.strip()]
+      child_nodes = []
+      if node.text:
+        formatted_text = self._PrettyPrintText(node.text, new_indent)
+        if formatted_text:
+          child_nodes.append(formatted_text)
+
+      for child in node:
+        child_output = self.PrettyPrintElementTreeNode(child, indent=new_indent)
+        if child_output.strip():
+          child_nodes.append(child_output)
+
+        if child.tail:
+          tail_text = self._PrettyPrintText(child.tail, new_indent)
+          if tail_text:
+            child_nodes.append(tail_text)
 
       # Determine whether we can fit the entire node on a single line.
-      close_tag = '</%s>' % node.tagName
+      close_tag = '</%s>' % node.tag
       space_left = WRAP_COLUMN - LastLineLength(s) - len(close_tag)
-      if (node.tagName in self.tags_that_allow_single_line and
-          len(child_nodes) == 1 and
-          len(child_nodes[0].strip()) <= space_left):
+      if (node.tag in self.tags_that_allow_single_line and
+          len(child_nodes) == 1 and len(child_nodes[0].strip()) <= space_left):
         s += child_nodes[0].strip()
       else:
         s += '\n' * newlines_after_open + '\n'.join(child_nodes)
@@ -284,11 +307,11 @@
     s += '\n' * newlines_after_close
     return s
 
-  def PrettyPrintNode(self, node, indent=0):
+  def PrettyPrintElementTreeNode(self, node, indent=0):
     """Pretty-prints the given XML node at the given indent level.
 
     Args:
-      node: The minidom node to pretty-print.
+      node: The ElementTree node to pretty-print.
       indent: The current indent level.
 
     Returns:
@@ -297,24 +320,9 @@
     Raises:
       Error: if the XML has unknown tags or attributes.
     """
-    # Handle the top-level document node.
-    if node.nodeType == xml.dom.minidom.Node.DOCUMENT_NODE:
-      return '\n'.join([self.PrettyPrintNode(n) for n in node.childNodes])
-
-    # Handle text nodes.
-    if node.nodeType == xml.dom.minidom.Node.TEXT_NODE:
-      return self._PrettyPrintText(node, indent)
+    # Handle comment nodes.
+    if node.tag is ET.Comment:
+      return '<!--%s-->\n' % node.text
 
     # Handle element nodes.
-    if node.nodeType == xml.dom.minidom.Node.ELEMENT_NODE:
-      return self._PrettyPrintElement(node, indent)
-
-    # Handle comment nodes.
-    if node.nodeType == xml.dom.minidom.Node.COMMENT_NODE:
-      return '<!--%s-->\n' % node.data
-
-    # Ignore other node types. This could be a processing instruction
-    # (<? ... ?>) or cdata section (<![CDATA[...]]!>), neither of which are
-    # legal in the histograms XML at present.
-    logging.error('Ignoring unrecognized node data: %s', node.toxml())
-    raise Error('Ignoring unrecognized node data: {0}'.format(node.toxml()))
+    return self._PrettyPrintElement(node, indent)
diff --git a/tools/metrics/histograms/enums.xml b/tools/metrics/histograms/enums.xml
index d5f4e40..dffcfde 100644
--- a/tools/metrics/histograms/enums.xml
+++ b/tools/metrics/histograms/enums.xml
@@ -32001,6 +32001,7 @@
   <int value="-2108564200" label="AutofillUpstream:disabled"/>
   <int value="-2104950596" label="HandwritingGesture:enabled"/>
   <int value="-2101682955" label="EnableNotificationIndicator:enabled"/>
+  <int value="-2101337189" label="AutofillOffNoServerData:disabled"/>
   <int value="-2099457894" label="Mash:enabled"/>
   <int value="-2099035488" label="enable-data-reduction-proxy-bypass-warning"/>
   <int value="-2098610409" label="disable-lcd-text"/>
@@ -33306,6 +33307,7 @@
   <int value="-88273414" label="ContentSuggestionsShowSummary:enabled"/>
   <int value="-86788587" label="allow-autofill-sync-credential"/>
   <int value="-86243376" label="LayoutNG:enabled"/>
+  <int value="-80501013" label="AutofillOffNoServerData:enabled"/>
   <int value="-80353187" label="disable-display-color-calibration"/>
   <int value="-79327236" label="ModeSpecificPowerButton:enabled"/>
   <int value="-78035185" label="custom_summary"/>
@@ -36466,6 +36468,13 @@
   <int value="2" label="MediaFling"/>
 </enum>
 
+<enum name="MediaNotificationSource">
+  <int value="0" label="Unknown"/>
+  <int value="1" label="Web"/>
+  <int value="2" label="Assistant"/>
+  <int value="3" label="Arc"/>
+</enum>
+
 <enum name="MediaOutputProtectionStatus">
   <int value="0" label="Queried"/>
   <int value="1" label="No external link"/>
diff --git a/tools/metrics/histograms/histograms.xml b/tools/metrics/histograms/histograms.xml
index cd630a0..ad0b4fdc 100644
--- a/tools/metrics/histograms/histograms.xml
+++ b/tools/metrics/histograms/histograms.xml
@@ -52038,6 +52038,16 @@
   </summary>
 </histogram>
 
+<histogram name="Media.Notification.Source" enum="MediaNotificationSource"
+    expires_after="2019-12-31">
+  <owner>beccahughes@chromium.org</owner>
+  <owner>media-dev@chromium.org</owner>
+  <summary>
+    The source of the underlying media session (e.g. arc, web) that displayed
+    the media notification. This is recorded when the notification is shown.
+  </summary>
+</histogram>
+
 <histogram name="Media.Notification.UserAction" enum="MediaSessionAction"
     expires_after="2019-12-31">
   <owner>beccahughes@chromium.org</owner>
@@ -126341,6 +126351,10 @@
 
 <histogram name="UMA.FileMetricsProvider.EmbeddedProfile.RecordTime" units="ms"
     expires_after="M71">
+  <obsolete>
+    Removed 2019/04 as times are small, consistent, and now on a background
+    thread.
+  </obsolete>
   <owner>bcwhite@chromium.org</owner>
   <summary>
     Tracks the time used to record all histograms from a file with an embedded
diff --git a/tools/metrics/histograms/histograms_print_style.py b/tools/metrics/histograms/histograms_print_style.py
index 5a44248..65828e6d 100644
--- a/tools/metrics/histograms/histograms_print_style.py
+++ b/tools/metrics/histograms/histograms_print_style.py
@@ -65,13 +65,13 @@
 # Tags that we allow to be squished into a single line for brevity.
 TAGS_THAT_ALLOW_SINGLE_LINE = ['summary', 'int', 'owner']
 
-LOWERCASE_NAME_FN = lambda n: n.attributes['name'].value.lower()
+LOWERCASE_NAME_FN = lambda n: n.get('name').lower()
 
 
 def _NaturalSortByName(node):
   """Sort by name, ordering numbers in the way humans expect."""
   # See: https://blog.codinghorror.com/sorting-for-humans-natural-sort-order/
-  name = node.attributes['name'].value.lower()
+  name = node.get('name').lower()
   convert = lambda text: int(text) if text.isdigit() else text
   return [convert(c) for c in re.split('([0-9]+)', name)]
 
@@ -82,7 +82,7 @@
 TAGS_ALPHABETIZATION_RULES = {
     'histograms': [('histogram', LOWERCASE_NAME_FN)],
     'enums': [('enum', LOWERCASE_NAME_FN)],
-    'enum': [('int', lambda n: int(n.attributes['value'].value))],
+    'enum': [('int', lambda n: int(n.get('value')))],
     'histogram_suffixes_list': [('histogram_suffixes', LOWERCASE_NAME_FN)],
     'histogram_suffixes': [
         ('obsolete', lambda n: None),
diff --git a/tools/metrics/histograms/pretty_print.py b/tools/metrics/histograms/pretty_print.py
index 1e96b4f4..dd57976d 100755
--- a/tools/metrics/histograms/pretty_print.py
+++ b/tools/metrics/histograms/pretty_print.py
@@ -18,19 +18,17 @@
 import os
 import shutil
 import sys
-import xml.dom.minidom
 
 sys.path.append(os.path.join(os.path.dirname(__file__), '..', 'common'))
 import diff_util
 import presubmit_util
 
+import etree_util
 import histograms_print_style
 
-
 class Error(Exception):
   pass
 
-
 UNIT_REWRITES = {
   'microsecond': 'microseconds',
   'us': 'microseconds',
@@ -49,34 +47,39 @@
   'percentage': '%',
 }
 
-
 def canonicalizeUnits(tree):
   """Canonicalize the spelling of certain units in histograms."""
-  histograms = tree.getElementsByTagName('histogram')
-  for histogram in histograms:
-    units = histogram.attributes.get('units')
-    if units and units.value in UNIT_REWRITES:
-      histogram.attributes['units'] = UNIT_REWRITES[units.value]
+  if tree.tag == 'histogram':
+    units = tree.get('units')
+    if units and units in UNIT_REWRITES:
+      tree.set('units', UNIT_REWRITES[units])
 
+  for child in tree:
+    canonicalizeUnits(child)
 
 def fixObsoleteOrder(tree):
   """Put obsolete tags at the beginning of histogram tags."""
-  histograms = tree.getElementsByTagName('histogram')
-  for histogram in histograms:
-    obsoletes = histogram.getElementsByTagName('obsolete')
-    if obsoletes:
-      histogram.insertBefore(obsoletes[0], histogram.firstChild)
-
+  for child in tree:
+    obsoletes = []
+    if child.tag == 'obsolete':
+      obsoletes.append(child)
+    for obsolete in obsoletes:
+      tree.remove(obsolete)
+      tree.insert(0, obsolete)
+    fixObsoleteOrder(child)
 
 def DropNodesByTagName(tree, tag):
   """Drop all nodes with named tag from the XML tree."""
-  nodes = tree.getElementsByTagName(tag)
-  for node in nodes:
-    node.parentNode.removeChild(node)
-
+  for child in tree:
+    removes = []
+    if child.tag == tag:
+      removes.append(child)
+    for child in removes:
+      tree.remove(child)
+    DropNodesByTagName(child, tag)
 
 def PrettyPrintHistograms(raw_xml):
-  """Pretty-print the given XML.
+  """Pretty-print the given histograms XML.
 
   Args:
     raw_xml: The contents of the histograms XML file, as a string.
@@ -84,43 +87,48 @@
   Returns:
     The pretty-printed version.
   """
-  tree = xml.dom.minidom.parseString(raw_xml)
-  return PrettyPrintHistogramsTree(tree)
-
+  top_level_content = etree_util.GetTopLevelContent(raw_xml)
+  root = etree_util.ParseXMLString(raw_xml)
+  return top_level_content + PrettyPrintHistogramsTree(root)
 
 def PrettyPrintHistogramsTree(tree):
-  """Pretty-print the given xml.dom.minidom.Document object.
+  """Pretty-print the given ElementTree element.
 
   Args:
-    tree: The xml.dom.minidom.Document object.
+    tree: The ElementTree element.
 
   Returns:
     The pretty-printed version as an XML string.
   """
-  assert isinstance(tree, xml.dom.minidom.Document)
   # Prevent accidentally adding enums to histograms.xml
   DropNodesByTagName(tree, 'enums')
   canonicalizeUnits(tree)
   fixObsoleteOrder(tree)
   return histograms_print_style.GetPrintStyle().PrettyPrintXml(tree)
 
-
 def PrettyPrintEnums(raw_xml):
-  """Pretty print the enums.xml file."""
-  tree = xml.dom.minidom.parseString(raw_xml)
-  # Prevent accidentally adding histograms to enums.xml
-  DropNodesByTagName(tree, 'histograms')
-  DropNodesByTagName(tree, 'histogram_suffixes_list')
-  return histograms_print_style.GetPrintStyle().PrettyPrintXml(tree)
+  """Pretty print the given enums XML."""
 
+  root = etree_util.ParseXMLString(raw_xml)
+
+  # Prevent accidentally adding histograms to enums.xml
+  DropNodesByTagName(root, 'histograms')
+  DropNodesByTagName(root, 'histogram_suffixes_list')
+
+  top_level_content = etree_util.GetTopLevelContent(raw_xml)
+
+  formatted_xml = (histograms_print_style.GetPrintStyle()
+                  .PrettyPrintXml(root))
+  return top_level_content + formatted_xml
 
 def main():
   status1 = presubmit_util.DoPresubmit(sys.argv, 'enums.xml',
                                        'enums.before.pretty-print.xml',
                                        'pretty_print.py', PrettyPrintEnums)
   status2 = presubmit_util.DoPresubmit(sys.argv, 'histograms.xml',
-                                       'histograms.before.pretty-print.xml',
-                                       'pretty_print.py', PrettyPrintHistograms)
+                                        'histograms.before.pretty-print.xml',
+                                        'pretty_print.py',
+                                        PrettyPrintHistograms)
   sys.exit(status1 or status2)
 
 if __name__ == '__main__':
diff --git a/tools/metrics/histograms/pretty_print_test.py b/tools/metrics/histograms/pretty_print_test.py
index 2df6cfcd..e0c1280c 100755
--- a/tools/metrics/histograms/pretty_print_test.py
+++ b/tools/metrics/histograms/pretty_print_test.py
@@ -9,30 +9,55 @@
 
 
 ORIGINAL_XML = """
+<!-- Top level Comment 1 -->
+<!-- Top level Comment 2 -->
 <histogram-configuration>
 <histograms>
- <histogram name="Test.Histogram" units="things">
+ <histogram name="Test.Histogram" units="us">
    <owner>person@chromium.org</owner>
    <summary>A long line that should be formatted in a way that does not result
      in extra whitespace between words.
+
+        It has multiple paragraphs.
    </summary>
+   Mixed content.
+   <obsolete>
+       Removed 1/2019.
+   </obsolete>
+ </histogram>
+ <histogram name="Foo.Bar" units="xxxxxxxxxxxxxxxxxxxyyyyyyyyyyyyyyyyyyyyyyzzzz">
+  <summary>Foo</summary>
  </histogram>
 </histograms>
+<enums>This shouldn't be here</enums>
 </histogram-configuration>
 """.strip()
 
 
 PRETTY_XML = """
+<!-- Top level Comment 1 -->
+<!-- Top level Comment 2 -->
+
 <histogram-configuration>
 
 <histograms>
 
-<histogram name="Test.Histogram" units="things">
+<histogram name="Foo.Bar" units="xxxxxxxxxxxxxxxxxxxyyyyyyyyyyyyyyyyyyyyyyzzzz">
+  <summary>Foo</summary>
+</histogram>
+
+<histogram name="Test.Histogram" units="microseconds">
+  <obsolete>
+    Removed 1/2019.
+  </obsolete>
   <owner>person@chromium.org</owner>
   <summary>
     A long line that should be formatted in a way that does not result in extra
     whitespace between words.
+
+    It has multiple paragraphs.
   </summary>
+  Mixed content.
 </histogram>
 
 </histograms>
@@ -43,11 +68,10 @@
 
 class PrettyPrintHistogramsXmlTest(unittest.TestCase):
 
-  def testWhitespaceWrapping(self):
+  def testPrettyPrinting(self):
     result = pretty_print.PrettyPrintHistograms(ORIGINAL_XML)
     self.maxDiff = None
     self.assertMultiLineEqual(PRETTY_XML, result.strip())
 
-
 if __name__ == '__main__':
   unittest.main()
diff --git a/tools/metrics/ukm/ukm_model.py b/tools/metrics/ukm/ukm_model.py
index a363d5c9..841257d 100644
--- a/tools/metrics/ukm/ukm_model.py
+++ b/tools/metrics/ukm/ukm_model.py
@@ -14,7 +14,7 @@
 _OWNER_TYPE = models.TextNodeType('owner', single_line=True)
 _SUMMARY_TYPE = models.TextNodeType('summary')
 
-_LOWERCASE_NAME_FN = lambda n: n.attributes['name'].value.lower()
+_LOWERCASE_NAME_FN = lambda n: n.get('name').lower()
 
 _ENUMERATION_TYPE = models.ObjectNodeType(
     'enumeration',
diff --git a/tools/perf/contrib/leak_detection/data/leak_detection.json b/tools/perf/contrib/leak_detection/data/leak_detection.json
index 1720684..25c0adb 100644
--- a/tools/perf/contrib/leak_detection/data/leak_detection.json
+++ b/tools/perf/contrib/leak_detection/data/leak_detection.json
@@ -1,612 +1,612 @@
 {
     "archives": {
         "http://a-rakumo.appspot.com": {
-            "DEFAULT": "leak_detection_004.wprgo"
+            "DEFAULT": "leak_detection_8da6995ea2.wprgo"
         },
         "http://europa.eu/": {
-            "DEFAULT": "leak_detection_004.wprgo"
+            "DEFAULT": "leak_detection_8da6995ea2.wprgo"
         },
         "http://game.co.za": {
-            "DEFAULT": "leak_detection_004.wprgo"
+            "DEFAULT": "leak_detection_8da6995ea2.wprgo"
         },
         "http://hdu.edu.cn": {
-            "DEFAULT": "leak_detection_004.wprgo"
+            "DEFAULT": "leak_detection_8da6995ea2.wprgo"
         },
         "http://infomoney.com.br": {
             "DEFAULT": "leak_detection_004.wprgo"
         },
         "http://kakaku.com/": {
-            "DEFAULT": "leak_detection_004.wprgo"
+            "DEFAULT": "leak_detection_8da6995ea2.wprgo"
         },
         "http://makfax.com.mk": {
-            "DEFAULT": "leak_detection_004.wprgo"
+            "DEFAULT": "leak_detection_8da6995ea2.wprgo"
         },
         "http://siteadvisor.com/": {
-            "DEFAULT": "leak_detection_004.wprgo"
+            "DEFAULT": "leak_detection_8da6995ea2.wprgo"
         },
         "http://support.wordpress.com": {
-            "DEFAULT": "leak_detection_004.wprgo"
+            "DEFAULT": "leak_detection_8da6995ea2.wprgo"
         },
         "http://whu.edu.cn": {
-            "DEFAULT": "leak_detection_004.wprgo"
+            "DEFAULT": "leak_detection_8da6995ea2.wprgo"
         },
         "http://www.22find.com": {
-            "DEFAULT": "leak_detection_004.wprgo"
+            "DEFAULT": "leak_detection_8da6995ea2.wprgo"
         },
         "http://www.520mojing.com": {
             "DEFAULT": "leak_detection_004.wprgo"
         },
         "http://www.aaannunci.it": {
-            "DEFAULT": "leak_detection_004.wprgo"
+            "DEFAULT": "leak_detection_8da6995ea2.wprgo"
         },
         "http://www.airbnb.ch": {
-            "DEFAULT": "leak_detection_004.wprgo"
+            "DEFAULT": "leak_detection_8da6995ea2.wprgo"
         },
         "http://www.aljazeera.net": {
-            "DEFAULT": "leak_detection_004.wprgo"
+            "DEFAULT": "leak_detection_8da6995ea2.wprgo"
         },
         "http://www.amazon.com": {
-            "DEFAULT": "leak_detection_004.wprgo"
+            "DEFAULT": "leak_detection_8da6995ea2.wprgo"
         },
         "http://www.ansa.it": {
-            "DEFAULT": "leak_detection_004.wprgo"
+            "DEFAULT": "leak_detection_8da6995ea2.wprgo"
         },
         "http://www.arabam.com": {
-            "DEFAULT": "leak_detection_004.wprgo"
+            "DEFAULT": "leak_detection_8da6995ea2.wprgo"
         },
         "http://www.benzworld.org": {
             "DEFAULT": "leak_detection_004.wprgo"
         },
         "http://www.besgold.com": {
-            "DEFAULT": "leak_detection_004.wprgo"
+            "DEFAULT": "leak_detection_8da6995ea2.wprgo"
         },
         "http://www.bestdic.ir": {
-            "DEFAULT": "leak_detection_004.wprgo"
+            "DEFAULT": "leak_detection_8da6995ea2.wprgo"
         },
         "http://www.block.io": {
             "DEFAULT": "leak_detection_004.wprgo"
         },
         "http://www.blu-ray.com": {
-            "DEFAULT": "leak_detection_004.wprgo"
+            "DEFAULT": "leak_detection_8da6995ea2.wprgo"
         },
         "http://www.careesma.in": {
-            "DEFAULT": "leak_detection_004.wprgo"
+            "DEFAULT": "leak_detection_8da6995ea2.wprgo"
         },
         "http://www.chatword.org": {
-            "DEFAULT": "leak_detection_004.wprgo"
+            "DEFAULT": "leak_detection_8da6995ea2.wprgo"
         },
         "http://www.cheapoair.com": {
-            "DEFAULT": "leak_detection_004.wprgo"
+            "DEFAULT": "leak_detection_8da6995ea2.wprgo"
         },
         "http://www.coco.fr": {
-            "DEFAULT": "leak_detection_004.wprgo"
+            "DEFAULT": "leak_detection_8da6995ea2.wprgo"
         },
         "http://www.contentful.com": {
-            "DEFAULT": "leak_detection_004.wprgo"
+            "DEFAULT": "leak_detection_8da6995ea2.wprgo"
         },
         "http://www.dailypost.ng": {
-            "DEFAULT": "leak_detection_004.wprgo"
+            "DEFAULT": "leak_detection_8da6995ea2.wprgo"
         },
         "http://www.daydao.com": {
-            "DEFAULT": "leak_detection_004.wprgo"
+            "DEFAULT": "leak_detection_8da6995ea2.wprgo"
         },
         "http://www.do-search.com": {
-            "DEFAULT": "leak_detection_004.wprgo"
+            "DEFAULT": "leak_detection_8da6995ea2.wprgo"
         },
         "http://www.e-shop.gr": {
-            "DEFAULT": "leak_detection_004.wprgo"
+            "DEFAULT": "leak_detection_8da6995ea2.wprgo"
         },
         "http://www.espn.com/": {
-            "DEFAULT": "leak_detection_004.wprgo"
+            "DEFAULT": "leak_detection_8da6995ea2.wprgo"
         },
         "http://www.estibot.com": {
-            "DEFAULT": "leak_detection_004.wprgo"
+            "DEFAULT": "leak_detection_8da6995ea2.wprgo"
         },
         "http://www.expediapartnercentral.com": {
-            "DEFAULT": "leak_detection_004.wprgo"
+            "DEFAULT": "leak_detection_8da6995ea2.wprgo"
         },
         "http://www.ezcardinfo.com": {
-            "DEFAULT": "leak_detection_004.wprgo"
+            "DEFAULT": "leak_detection_8da6995ea2.wprgo"
         },
         "http://www.foxnews.com/": {
-            "DEFAULT": "leak_detection_004.wprgo"
+            "DEFAULT": "leak_detection_8da6995ea2.wprgo"
         },
         "http://www.goal.com/": {
-            "DEFAULT": "leak_detection_004.wprgo"
+            "DEFAULT": "leak_detection_8da6995ea2.wprgo"
         },
         "http://www.googleapps.com": {
-            "DEFAULT": "leak_detection_004.wprgo"
+            "DEFAULT": "leak_detection_8da6995ea2.wprgo"
         },
         "http://www.gradpoint.com": {
-            "DEFAULT": "leak_detection_004.wprgo"
+            "DEFAULT": "leak_detection_8da6995ea2.wprgo"
         },
         "http://www.gulfair.com": {
-            "DEFAULT": "leak_detection_004.wprgo"
+            "DEFAULT": "leak_detection_8da6995ea2.wprgo"
         },
         "http://www.highwaybus.com": {
-            "DEFAULT": "leak_detection_004.wprgo"
+            "DEFAULT": "leak_detection_8da6995ea2.wprgo"
         },
         "http://www.hockeybuzz.com": {
-            "DEFAULT": "leak_detection_004.wprgo"
+            "DEFAULT": "leak_detection_8da6995ea2.wprgo"
         },
         "http://www.horairetrain.net": {
-            "DEFAULT": "leak_detection_004.wprgo"
+            "DEFAULT": "leak_detection_8da6995ea2.wprgo"
         },
         "http://www.inbank.it": {
-            "DEFAULT": "leak_detection_004.wprgo"
+            "DEFAULT": "leak_detection_8da6995ea2.wprgo"
         },
         "http://www.indeed.com": {
-            "DEFAULT": "leak_detection_004.wprgo"
+            "DEFAULT": "leak_detection_8da6995ea2.wprgo"
         },
         "http://www.iowacconline.com": {
-            "DEFAULT": "leak_detection_004.wprgo"
+            "DEFAULT": "leak_detection_8da6995ea2.wprgo"
         },
         "http://www.jd.com/": {
-            "DEFAULT": "leak_detection_004.wprgo"
+            "DEFAULT": "leak_detection_8da6995ea2.wprgo"
         },
         "http://www.leandomainsearch.com": {
-            "DEFAULT": "leak_detection_004.wprgo"
+            "DEFAULT": "leak_detection_8da6995ea2.wprgo"
         },
         "http://www.listindiario.com": {
-            "DEFAULT": "leak_detection_004.wprgo"
+            "DEFAULT": "leak_detection_8da6995ea2.wprgo"
         },
         "http://www.livedoor.jp": {
-            "DEFAULT": "leak_detection_004.wprgo"
+            "DEFAULT": "leak_detection_8da6995ea2.wprgo"
         },
         "http://www.magnetmail.net": {
-            "DEFAULT": "leak_detection_004.wprgo"
+            "DEFAULT": "leak_detection_8da6995ea2.wprgo"
         },
         "http://www.mail.bg": {
-            "DEFAULT": "leak_detection_004.wprgo"
+            "DEFAULT": "leak_detection_8da6995ea2.wprgo"
         },
         "http://www.mastodon.social": {
-            "DEFAULT": "leak_detection_004.wprgo"
+            "DEFAULT": "leak_detection_8da6995ea2.wprgo"
         },
         "http://www.mbs.de": {
-            "DEFAULT": "leak_detection_004.wprgo"
+            "DEFAULT": "leak_detection_8da6995ea2.wprgo"
         },
         "http://www.nicovideo.jp/": {
-            "DEFAULT": "leak_detection_004.wprgo"
+            "DEFAULT": "leak_detection_8da6995ea2.wprgo"
         },
         "http://www.nigma.ru": {
-            "DEFAULT": "leak_detection_004.wprgo"
+            "DEFAULT": "leak_detection_8da6995ea2.wprgo"
         },
         "http://www.nusatrip.com": {
-            "DEFAULT": "leak_detection_004.wprgo"
+            "DEFAULT": "leak_detection_8da6995ea2.wprgo"
         },
         "http://www.omniboxes.com": {
-            "DEFAULT": "leak_detection_004.wprgo"
+            "DEFAULT": "leak_detection_8da6995ea2.wprgo"
         },
         "http://www.onlinedown.net": {
-            "DEFAULT": "leak_detection_004.wprgo"
+            "DEFAULT": "leak_detection_8da6995ea2.wprgo"
         },
         "http://www.opentable.com": {
-            "DEFAULT": "leak_detection_004.wprgo"
+            "DEFAULT": "leak_detection_8da6995ea2.wprgo"
         },
         "http://www.pingpang.info": {
-            "DEFAULT": "leak_detection_004.wprgo"
+            "DEFAULT": "leak_detection_8da6995ea2.wprgo"
         },
         "http://www.podbay.fm": {
-            "DEFAULT": "leak_detection_004.wprgo"
+            "DEFAULT": "leak_detection_8da6995ea2.wprgo"
         },
         "http://www.privacyassistant.net": {
-            "DEFAULT": "leak_detection_004.wprgo"
+            "DEFAULT": "leak_detection_8da6995ea2.wprgo"
         },
         "http://www.psiexams.com": {
-            "DEFAULT": "leak_detection_004.wprgo"
+            "DEFAULT": "leak_detection_8da6995ea2.wprgo"
         },
         "http://www.qq.com": {
-            "DEFAULT": "leak_detection_004.wprgo"
+            "DEFAULT": "leak_detection_8da6995ea2.wprgo"
         },
         "http://www.quora.com": {
-            "DEFAULT": "leak_detection_004.wprgo"
+            "DEFAULT": "leak_detection_8da6995ea2.wprgo"
         },
         "http://www.railsguides.jp": {
-            "DEFAULT": "leak_detection_004.wprgo"
+            "DEFAULT": "leak_detection_8da6995ea2.wprgo"
         },
         "http://www.register.com": {
-            "DEFAULT": "leak_detection_004.wprgo"
+            "DEFAULT": "leak_detection_8da6995ea2.wprgo"
         },
         "http://www.reverso.net/": {
-            "DEFAULT": "leak_detection_004.wprgo"
+            "DEFAULT": "leak_detection_8da6995ea2.wprgo"
         },
         "http://www.sakurafile.com": {
-            "DEFAULT": "leak_detection_004.wprgo"
+            "DEFAULT": "leak_detection_8da6995ea2.wprgo"
         },
         "http://www.samsung-fun.ru": {
-            "DEFAULT": "leak_detection_004.wprgo"
+            "DEFAULT": "leak_detection_8da6995ea2.wprgo"
         },
         "http://www.savaari.com": {
-            "DEFAULT": "leak_detection_004.wprgo"
+            "DEFAULT": "leak_detection_8da6995ea2.wprgo"
         },
         "http://www.sick.com": {
-            "DEFAULT": "leak_detection_004.wprgo"
+            "DEFAULT": "leak_detection_8da6995ea2.wprgo"
         },
         "http://www.silverpop.com": {
-            "DEFAULT": "leak_detection_004.wprgo"
+            "DEFAULT": "leak_detection_8da6995ea2.wprgo"
         },
         "http://www.sjp.pl": {
-            "DEFAULT": "leak_detection_004.wprgo"
+            "DEFAULT": "leak_detection_8da6995ea2.wprgo"
         },
         "http://www.skipaas.com": {
-            "DEFAULT": "leak_detection_004.wprgo"
+            "DEFAULT": "leak_detection_8da6995ea2.wprgo"
         },
         "http://www.sklavenzentrale.com": {
-            "DEFAULT": "leak_detection_004.wprgo"
+            "DEFAULT": "leak_detection_8da6995ea2.wprgo"
         },
         "http://www.time.com": {
-            "DEFAULT": "leak_detection_004.wprgo"
+            "DEFAULT": "leak_detection_8da6995ea2.wprgo"
         },
         "http://www.torrentzeu.to": {
-            "DEFAULT": "leak_detection_004.wprgo"
+            "DEFAULT": "leak_detection_8da6995ea2.wprgo"
         },
         "http://www.twitter.com": {
-            "DEFAULT": "leak_detection_004.wprgo"
+            "DEFAULT": "leak_detection_8da6995ea2.wprgo"
         },
         "http://www.webwebweb.com": {
-            "DEFAULT": "leak_detection_004.wprgo"
+            "DEFAULT": "leak_detection_8da6995ea2.wprgo"
         },
         "http://www.wikia.com/": {
-            "DEFAULT": "leak_detection_004.wprgo"
+            "DEFAULT": "leak_detection_8da6995ea2.wprgo"
         },
         "http://www.wpjam.com": {
-            "DEFAULT": "leak_detection_004.wprgo"
+            "DEFAULT": "leak_detection_8da6995ea2.wprgo"
         },
         "http://www.zhengjie.com": {
-            "DEFAULT": "leak_detection_004.wprgo"
+            "DEFAULT": "leak_detection_8da6995ea2.wprgo"
         },
         "http://zzz.com.ua": {
-            "DEFAULT": "leak_detection_004.wprgo"
+            "DEFAULT": "leak_detection_8da6995ea2.wprgo"
         },
         "https://9gag.com/": {
-            "DEFAULT": "leak_detection_004.wprgo"
+            "DEFAULT": "leak_detection_8da6995ea2.wprgo"
         },
         "https://ameblo.jp/": {
-            "DEFAULT": "leak_detection_004.wprgo"
+            "DEFAULT": "leak_detection_8da6995ea2.wprgo"
         },
         "https://answers.yahoo.com/": {
-            "DEFAULT": "leak_detection_004.wprgo"
+            "DEFAULT": "leak_detection_8da6995ea2.wprgo"
         },
         "https://archive.org/": {
-            "DEFAULT": "leak_detection_004.wprgo"
+            "DEFAULT": "leak_detection_8da6995ea2.wprgo"
         },
         "https://billdesk.com/": {
-            "DEFAULT": "leak_detection_004.wprgo"
+            "DEFAULT": "leak_detection_8da6995ea2.wprgo"
         },
         "https://docs.google.com/": {
-            "DEFAULT": "leak_detection_004.wprgo"
+            "DEFAULT": "leak_detection_8da6995ea2.wprgo"
         },
         "https://edition.cnn.com/": {
-            "DEFAULT": "leak_detection_004.wprgo"
+            "DEFAULT": "leak_detection_8da6995ea2.wprgo"
         },
         "https://fc2.com/": {
-            "DEFAULT": "leak_detection_004.wprgo"
+            "DEFAULT": "leak_detection_8da6995ea2.wprgo"
         },
         "https://github.com/": {
-            "DEFAULT": "leak_detection_004.wprgo"
+            "DEFAULT": "leak_detection_8da6995ea2.wprgo"
         },
         "https://gizmodo.com/": {
             "DEFAULT": "leak_detection_004.wprgo"
         },
         "https://ign.com/": {
-            "DEFAULT": "leak_detection_004.wprgo"
+            "DEFAULT": "leak_detection_8da6995ea2.wprgo"
         },
         "https://imgur.com/": {
-            "DEFAULT": "leak_detection_004.wprgo"
+            "DEFAULT": "leak_detection_8da6995ea2.wprgo"
         },
         "https://mail.ru/": {
-            "DEFAULT": "leak_detection_004.wprgo"
+            "DEFAULT": "leak_detection_8da6995ea2.wprgo"
         },
         "https://news.google.com/": {
-            "DEFAULT": "leak_detection_004.wprgo"
+            "DEFAULT": "leak_detection_8da6995ea2.wprgo"
         },
         "https://outlook.live.com": {
-            "DEFAULT": "leak_detection_004.wprgo"
+            "DEFAULT": "leak_detection_8da6995ea2.wprgo"
         },
         "https://play.google.com/store": {
-            "DEFAULT": "leak_detection_004.wprgo"
+            "DEFAULT": "leak_detection_8da6995ea2.wprgo"
         },
         "https://play.na.leagueoflegends.com/": {
-            "DEFAULT": "leak_detection_004.wprgo"
+            "DEFAULT": "leak_detection_8da6995ea2.wprgo"
         },
         "https://porn555.com/": {
-            "DEFAULT": "leak_detection_004.wprgo"
+            "DEFAULT": "leak_detection_8da6995ea2.wprgo"
         },
         "https://soundcloud.com/": {
-            "DEFAULT": "leak_detection_004.wprgo"
+            "DEFAULT": "leak_detection_8da6995ea2.wprgo"
         },
         "https://stackoverflow.com/": {
-            "DEFAULT": "leak_detection_004.wprgo"
+            "DEFAULT": "leak_detection_8da6995ea2.wprgo"
         },
         "https://translate.google.com/": {
-            "DEFAULT": "leak_detection_004.wprgo"
+            "DEFAULT": "leak_detection_8da6995ea2.wprgo"
         },
         "https://uidai.gov.in/": {
-            "DEFAULT": "leak_detection_004.wprgo"
+            "DEFAULT": "leak_detection_8da6995ea2.wprgo"
         },
         "https://vimeo.com/": {
-            "DEFAULT": "leak_detection_004.wprgo"
+            "DEFAULT": "leak_detection_8da6995ea2.wprgo"
         },
         "https://vk.com/": {
-            "DEFAULT": "leak_detection_004.wprgo"
+            "DEFAULT": "leak_detection_8da6995ea2.wprgo"
         },
         "https://weather.com/": {
-            "DEFAULT": "leak_detection_004.wprgo"
+            "DEFAULT": "leak_detection_8da6995ea2.wprgo"
         },
         "https://weibo.com/": {
-            "DEFAULT": "leak_detection_004.wprgo"
+            "DEFAULT": "leak_detection_8da6995ea2.wprgo"
         },
         "https://wordpress.com/": {
-            "DEFAULT": "leak_detection_004.wprgo"
+            "DEFAULT": "leak_detection_8da6995ea2.wprgo"
         },
         "https://world.taobao.com/": {
-            "DEFAULT": "leak_detection_004.wprgo"
+            "DEFAULT": "leak_detection_8da6995ea2.wprgo"
         },
         "https://www.360.cn/": {
-            "DEFAULT": "leak_detection_004.wprgo"
+            "DEFAULT": "leak_detection_8da6995ea2.wprgo"
         },
         "https://www.aliexpress.com/": {
-            "DEFAULT": "leak_detection_004.wprgo"
+            "DEFAULT": "leak_detection_8da6995ea2.wprgo"
         },
         "https://www.alipay.com/": {
-            "DEFAULT": "leak_detection_004.wprgo"
+            "DEFAULT": "leak_detection_8da6995ea2.wprgo"
         },
         "https://www.apple.com/": {
-            "DEFAULT": "leak_detection_004.wprgo"
+            "DEFAULT": "leak_detection_8da6995ea2.wprgo"
         },
         "https://www.baidu.com": {
-            "DEFAULT": "leak_detection_004.wprgo"
+            "DEFAULT": "leak_detection_8da6995ea2.wprgo"
         },
         "https://www.bestbuy.com/": {
-            "DEFAULT": "leak_detection_004.wprgo"
+            "DEFAULT": "leak_detection_8da6995ea2.wprgo"
         },
         "https://www.bing.com/": {
-            "DEFAULT": "leak_detection_004.wprgo"
+            "DEFAULT": "leak_detection_8da6995ea2.wprgo"
         },
         "https://www.blizzard.com": {
-            "DEFAULT": "leak_detection_004.wprgo"
+            "DEFAULT": "leak_detection_8da6995ea2.wprgo"
         },
         "https://www.blogger.com/": {
-            "DEFAULT": "leak_detection_004.wprgo"
+            "DEFAULT": "leak_detection_8da6995ea2.wprgo"
         },
         "https://www.booking.com/": {
-            "DEFAULT": "leak_detection_004.wprgo"
+            "DEFAULT": "leak_detection_8da6995ea2.wprgo"
         },
         "https://www.chase.com/": {
-            "DEFAULT": "leak_detection_004.wprgo"
+            "DEFAULT": "leak_detection_8da6995ea2.wprgo"
         },
         "https://www.citi.com": {
             "DEFAULT": "leak_detection_002.wprgo"
         },
         "https://www.craigslist.org/": {
-            "DEFAULT": "leak_detection_004.wprgo"
+            "DEFAULT": "leak_detection_8da6995ea2.wprgo"
         },
         "https://www.cricbuzz.com/": {
-            "DEFAULT": "leak_detection_004.wprgo"
+            "DEFAULT": "leak_detection_8da6995ea2.wprgo"
         },
         "https://www.csdn.net/": {
-            "DEFAULT": "leak_detection_004.wprgo"
+            "DEFAULT": "leak_detection_8da6995ea2.wprgo"
         },
         "https://www.deviantart.com/": {
-            "DEFAULT": "leak_detection_004.wprgo"
+            "DEFAULT": "leak_detection_8da6995ea2.wprgo"
         },
         "https://www.dropbox.com/": {
-            "DEFAULT": "leak_detection_004.wprgo"
+            "DEFAULT": "leak_detection_8da6995ea2.wprgo"
         },
         "https://www.ebay.com/": {
-            "DEFAULT": "leak_detection_004.wprgo"
+            "DEFAULT": "leak_detection_8da6995ea2.wprgo"
         },
         "https://www.engadget.com/": {
-            "DEFAULT": "leak_detection_004.wprgo"
+            "DEFAULT": "leak_detection_8da6995ea2.wprgo"
         },
         "https://www.epicgames.com/": {
-            "DEFAULT": "leak_detection_004.wprgo"
+            "DEFAULT": "leak_detection_8da6995ea2.wprgo"
         },
         "https://www.etsy.com/": {
-            "DEFAULT": "leak_detection_004.wprgo"
+            "DEFAULT": "leak_detection_8da6995ea2.wprgo"
         },
         "https://www.expedia.com/": {
-            "DEFAULT": "leak_detection_004.wprgo"
+            "DEFAULT": "leak_detection_8da6995ea2.wprgo"
         },
         "https://www.facebook.com": {
-            "DEFAULT": "leak_detection_004.wprgo"
+            "DEFAULT": "leak_detection_8da6995ea2.wprgo"
         },
         "https://www.flipkart.com/": {
-            "DEFAULT": "leak_detection_004.wprgo"
+            "DEFAULT": "leak_detection_8da6995ea2.wprgo"
         },
         "https://www.gamespot.com/": {
-            "DEFAULT": "leak_detection_004.wprgo"
+            "DEFAULT": "leak_detection_8da6995ea2.wprgo"
         },
         "https://www.goo.ne.jp/": {
-            "DEFAULT": "leak_detection_004.wprgo"
+            "DEFAULT": "leak_detection_8da6995ea2.wprgo"
         },
         "https://www.goodreads.com/": {
-            "DEFAULT": "leak_detection_004.wprgo"
+            "DEFAULT": "leak_detection_8da6995ea2.wprgo"
         },
         "https://www.google.com": {
-            "DEFAULT": "leak_detection_004.wprgo"
+            "DEFAULT": "leak_detection_8da6995ea2.wprgo"
         },
         "https://www.gsmarena.com/": {
-            "DEFAULT": "leak_detection_004.wprgo"
+            "DEFAULT": "leak_detection_8da6995ea2.wprgo"
         },
         "https://www.hdfcbank.com/": {
-            "DEFAULT": "leak_detection_004.wprgo"
+            "DEFAULT": "leak_detection_8da6995ea2.wprgo"
         },
         "https://www.homedepot.com/": {
-            "DEFAULT": "leak_detection_004.wprgo"
+            "DEFAULT": "leak_detection_8da6995ea2.wprgo"
         },
         "https://www.hotels.com/": {
-            "DEFAULT": "leak_detection_004.wprgo"
+            "DEFAULT": "leak_detection_8da6995ea2.wprgo"
         },
         "https://www.hotstar.com/": {
-            "DEFAULT": "leak_detection_004.wprgo"
+            "DEFAULT": "leak_detection_8da6995ea2.wprgo"
         },
         "https://www.hulu.com/": {
-            "DEFAULT": "leak_detection_004.wprgo"
+            "DEFAULT": "leak_detection_8da6995ea2.wprgo"
         },
         "https://www.icicibank.com/": {
-            "DEFAULT": "leak_detection_004.wprgo"
+            "DEFAULT": "leak_detection_8da6995ea2.wprgo"
         },
         "https://www.ikea.com/": {
-            "DEFAULT": "leak_detection_004.wprgo"
+            "DEFAULT": "leak_detection_8da6995ea2.wprgo"
         },
         "https://www.imdb.com/": {
-            "DEFAULT": "leak_detection_004.wprgo"
+            "DEFAULT": "leak_detection_8da6995ea2.wprgo"
         },
         "https://www.incometaxindiaefiling.gov.in/": {
-            "DEFAULT": "leak_detection_004.wprgo"
+            "DEFAULT": "leak_detection_8da6995ea2.wprgo"
         },
         "https://www.indiatimes.com/": {
-            "DEFAULT": "leak_detection_004.wprgo"
+            "DEFAULT": "leak_detection_8da6995ea2.wprgo"
         },
         "https://www.instagram.com/": {
-            "DEFAULT": "leak_detection_004.wprgo"
+            "DEFAULT": "leak_detection_8da6995ea2.wprgo"
         },
         "https://www.irctc.co.in/nget/": {
-            "DEFAULT": "leak_detection_004.wprgo"
+            "DEFAULT": "leak_detection_8da6995ea2.wprgo"
         },
         "https://www.jw.org/": {
-            "DEFAULT": "leak_detection_004.wprgo"
+            "DEFAULT": "leak_detection_8da6995ea2.wprgo"
         },
         "https://www.linkedin.com/": {
-            "DEFAULT": "leak_detection_004.wprgo"
+            "DEFAULT": "leak_detection_8da6995ea2.wprgo"
         },
         "https://www.livejournal.com/": {
-            "DEFAULT": "leak_detection_004.wprgo"
+            "DEFAULT": "leak_detection_8da6995ea2.wprgo"
         },
         "https://www.macys.com": {
-            "DEFAULT": "leak_detection_004.wprgo"
+            "DEFAULT": "leak_detection_8da6995ea2.wprgo"
         },
         "https://www.mediafire.com/": {
-            "DEFAULT": "leak_detection_004.wprgo"
+            "DEFAULT": "leak_detection_8da6995ea2.wprgo"
         },
         "https://www.microsoft.com/": {
-            "DEFAULT": "leak_detection_004.wprgo"
+            "DEFAULT": "leak_detection_8da6995ea2.wprgo"
         },
         "https://www.msn.com/": {
-            "DEFAULT": "leak_detection_004.wprgo"
+            "DEFAULT": "leak_detection_8da6995ea2.wprgo"
         },
         "https://www.netflix.com/": {
-            "DEFAULT": "leak_detection_004.wprgo"
+            "DEFAULT": "leak_detection_8da6995ea2.wprgo"
         },
         "https://www.nlm.nih.gov/": {
-            "DEFAULT": "leak_detection_004.wprgo"
+            "DEFAULT": "leak_detection_8da6995ea2.wprgo"
         },
         "https://www.nytimes.com/": {
-            "DEFAULT": "leak_detection_004.wprgo"
+            "DEFAULT": "leak_detection_8da6995ea2.wprgo"
         },
         "https://www.office.com/": {
-            "DEFAULT": "leak_detection_004.wprgo"
+            "DEFAULT": "leak_detection_8da6995ea2.wprgo"
         },
         "https://www.onlinesbi.com/": {
-            "DEFAULT": "leak_detection_004.wprgo"
+            "DEFAULT": "leak_detection_8da6995ea2.wprgo"
         },
         "https://www.patreon.com/": {
-            "DEFAULT": "leak_detection_004.wprgo"
+            "DEFAULT": "leak_detection_8da6995ea2.wprgo"
         },
         "https://www.paypal.com/": {
-            "DEFAULT": "leak_detection_004.wprgo"
+            "DEFAULT": "leak_detection_8da6995ea2.wprgo"
         },
         "https://www.pinterest.com/": {
-            "DEFAULT": "leak_detection_004.wprgo"
+            "DEFAULT": "leak_detection_8da6995ea2.wprgo"
         },
         "https://www.pixiv.net/": {
-            "DEFAULT": "leak_detection_004.wprgo"
+            "DEFAULT": "leak_detection_8da6995ea2.wprgo"
         },
         "https://www.pornhub.com/": {
-            "DEFAULT": "leak_detection_004.wprgo"
+            "DEFAULT": "leak_detection_8da6995ea2.wprgo"
         },
         "https://www.prezi.com": {
-            "DEFAULT": "leak_detection_004.wprgo"
+            "DEFAULT": "leak_detection_8da6995ea2.wprgo"
         },
         "https://www.rakuten.co.jp/": {
-            "DEFAULT": "leak_detection_004.wprgo"
+            "DEFAULT": "leak_detection_8da6995ea2.wprgo"
         },
         "https://www.reddit.com": {
             "DEFAULT": "leak_detection_003.wprgo"
         },
         "https://www.reddit.com/": {
-            "DEFAULT": "leak_detection_004.wprgo"
+            "DEFAULT": "leak_detection_8da6995ea2.wprgo"
         },
         "https://www.roblox.com/": {
-            "DEFAULT": "leak_detection_004.wprgo"
+            "DEFAULT": "leak_detection_8da6995ea2.wprgo"
         },
         "https://www.salesforce.com/": {
-            "DEFAULT": "leak_detection_004.wprgo"
+            "DEFAULT": "leak_detection_8da6995ea2.wprgo"
         },
         "https://www.scribd.com/": {
-            "DEFAULT": "leak_detection_004.wprgo"
+            "DEFAULT": "leak_detection_8da6995ea2.wprgo"
         },
         "https://www.shutterstock.com/": {
-            "DEFAULT": "leak_detection_004.wprgo"
+            "DEFAULT": "leak_detection_8da6995ea2.wprgo"
         },
         "https://www.sina.com.cn/": {
-            "DEFAULT": "leak_detection_004.wprgo"
+            "DEFAULT": "leak_detection_8da6995ea2.wprgo"
         },
         "https://www.slideshare.net/": {
-            "DEFAULT": "leak_detection_004.wprgo"
+            "DEFAULT": "leak_detection_8da6995ea2.wprgo"
         },
         "https://www.sohu.com/": {
-            "DEFAULT": "leak_detection_004.wprgo"
+            "DEFAULT": "leak_detection_8da6995ea2.wprgo"
         },
         "https://www.target.com/": {
-            "DEFAULT": "leak_detection_004.wprgo"
+            "DEFAULT": "leak_detection_8da6995ea2.wprgo"
         },
         "https://www.theguardian.com/": {
-            "DEFAULT": "leak_detection_004.wprgo"
+            "DEFAULT": "leak_detection_8da6995ea2.wprgo"
         },
         "https://www.thesaurus.com/": {
-            "DEFAULT": "leak_detection_004.wprgo"
+            "DEFAULT": "leak_detection_8da6995ea2.wprgo"
         },
         "https://www.theverge.com/": {
-            "DEFAULT": "leak_detection_004.wprgo"
+            "DEFAULT": "leak_detection_8da6995ea2.wprgo"
         },
         "https://www.tmall.com/": {
-            "DEFAULT": "leak_detection_004.wprgo"
+            "DEFAULT": "leak_detection_8da6995ea2.wprgo"
         },
         "https://www.tripadvisor.com/": {
-            "DEFAULT": "leak_detection_004.wprgo"
+            "DEFAULT": "leak_detection_8da6995ea2.wprgo"
         },
         "https://www.tumblr.com/": {
             "DEFAULT": "leak_detection_004.wprgo"
         },
         "https://www.twitch.tv/": {
-            "DEFAULT": "leak_detection_004.wprgo"
+            "DEFAULT": "leak_detection_8da6995ea2.wprgo"
         },
         "https://www.udemy.com/": {
-            "DEFAULT": "leak_detection_004.wprgo"
+            "DEFAULT": "leak_detection_8da6995ea2.wprgo"
         },
         "https://www.weebly.com/": {
-            "DEFAULT": "leak_detection_004.wprgo"
+            "DEFAULT": "leak_detection_8da6995ea2.wprgo"
         },
         "https://www.whatsapp.com/": {
-            "DEFAULT": "leak_detection_004.wprgo"
+            "DEFAULT": "leak_detection_8da6995ea2.wprgo"
         },
         "https://www.wikipedia.org": {
-            "DEFAULT": "leak_detection_004.wprgo"
+            "DEFAULT": "leak_detection_8da6995ea2.wprgo"
         },
         "https://www.xfinity.com/": {
-            "DEFAULT": "leak_detection_004.wprgo"
+            "DEFAULT": "leak_detection_8da6995ea2.wprgo"
         },
         "https://www.xvideos.com/": {
-            "DEFAULT": "leak_detection_004.wprgo"
+            "DEFAULT": "leak_detection_8da6995ea2.wprgo"
         },
         "https://www.yahoo.co.jp/": {
-            "DEFAULT": "leak_detection_004.wprgo"
+            "DEFAULT": "leak_detection_8da6995ea2.wprgo"
         },
         "https://www.yahoo.com": {
-            "DEFAULT": "leak_detection_004.wprgo"
+            "DEFAULT": "leak_detection_8da6995ea2.wprgo"
         },
         "https://www.yelp.com/": {
-            "DEFAULT": "leak_detection_004.wprgo"
+            "DEFAULT": "leak_detection_8da6995ea2.wprgo"
         },
         "https://www.youtube.com": {
-            "DEFAULT": "leak_detection_004.wprgo"
+            "DEFAULT": "leak_detection_8da6995ea2.wprgo"
         },
         "https://yandex.ru/": {
-            "DEFAULT": "leak_detection_004.wprgo"
+            "DEFAULT": "leak_detection_8da6995ea2.wprgo"
         }
     },
     "description": "Describes the Web Page Replay archives for a story set. Don't edit by hand! Use record_wpr for updating.",
     "platform_specific": true
-}
+}
\ No newline at end of file
diff --git a/tools/perf/contrib/leak_detection/data/leak_detection_8da6995ea2.wprgo b/tools/perf/contrib/leak_detection/data/leak_detection_8da6995ea2.wprgo
new file mode 100644
index 0000000..36e56d9
--- /dev/null
+++ b/tools/perf/contrib/leak_detection/data/leak_detection_8da6995ea2.wprgo
Binary files differ
diff --git a/tools/perf/contrib/leak_detection/data/leak_detection_8da6995ea2.wprgo.sha1 b/tools/perf/contrib/leak_detection/data/leak_detection_8da6995ea2.wprgo.sha1
new file mode 100644
index 0000000..db8e2b9
--- /dev/null
+++ b/tools/perf/contrib/leak_detection/data/leak_detection_8da6995ea2.wprgo.sha1
@@ -0,0 +1 @@
+8da6995ea2940f144c11fbfc24d0e10d4715e9cc
\ No newline at end of file
diff --git a/ui/aura/test/ui_controls_factory_ozone.cc b/ui/aura/test/ui_controls_factory_ozone.cc
index f2bd4f9..c729b40a 100644
--- a/ui/aura/test/ui_controls_factory_ozone.cc
+++ b/ui/aura/test/ui_controls_factory_ozone.cc
@@ -25,6 +25,7 @@
 #include "ui/display/screen.h"
 #include "ui/events/event_utils.h"
 #include "ui/events/test/events_test_utils.h"
+#include "ui/gfx/geometry/point_conversions.h"
 
 namespace aura {
 namespace test {
@@ -129,7 +130,7 @@
   bool SendMouseMoveNotifyWhenDone(long screen_x,
                                    long screen_y,
                                    base::OnceClosure closure) override {
-    gfx::Point host_location(screen_x, screen_y);
+    gfx::PointF host_location(screen_x, screen_y);
     int64_t display_id = display::kInvalidDisplayId;
     if (!ScreenDIPToHostPixels(&host_location, &display_id))
       return false;
@@ -158,13 +159,14 @@
                                      int button_state,
                                      base::OnceClosure closure,
                                      int accelerator_state) override {
-    gfx::Point host_location;
+    gfx::PointF host_location;
     int64_t display_id = display::kInvalidDisplayId;
     if (last_mouse_location_.has_value()) {
       host_location = last_mouse_location_.value();
       display_id = last_mouse_display_id_;
     } else {
-      host_location = host_->window()->env()->last_mouse_location();
+      host_location =
+          gfx::PointF(host_->window()->env()->last_mouse_location());
       if (!ScreenDIPToHostPixels(&host_location, &display_id))
         return false;
     }
@@ -228,7 +230,7 @@
                                      int y,
                                      base::OnceClosure task) override {
     DCHECK_NE(0, action);
-    gfx::Point host_location(x, y);
+    gfx::PointF host_location(x, y);
     int64_t display_id = display::kInvalidDisplayId;
     if (!ScreenDIPToHostPixels(&host_location, &display_id))
       return false;
@@ -263,8 +265,24 @@
                        int64_t display_id,
                        base::OnceClosure closure) {
     if (event_injector_) {
+      auto event_to_inject = ui::Event::Clone(*event);
+
+      if (event_to_inject->IsLocatedEvent()) {
+        // EventInjector expects coordinates relative to host and in DIPs.
+        display::Display display;
+        CHECK(display::Screen::GetScreen()->GetDisplayWithDisplayId(display_id,
+                                                                    &display));
+
+        ui::LocatedEvent* located_event = event_to_inject->AsLocatedEvent();
+        gfx::PointF location_in_host_dip = gfx::ScalePoint(
+            located_event->location_f(), 1 / display.device_scale_factor(),
+            1 / display.device_scale_factor());
+        located_event->set_location_f(location_in_host_dip);
+        located_event->set_root_location_f(location_in_host_dip);
+      }
+
       event_injector_->InjectEvent(
-          display_id, ui::Event::Clone(*event),
+          display_id, std::move(event_to_inject),
           base::BindOnce(&OnWindowServiceProcessedEvent, std::move(closure)));
       return;
     }
@@ -304,7 +322,7 @@
   }
 
   void PostMouseEvent(ui::EventType type,
-                      const gfx::Point& host_location,
+                      const gfx::PointF& host_location,
                       int flags,
                       int changed_button_flags,
                       int64_t display_id,
@@ -317,7 +335,7 @@
   }
 
   void PostMouseEventTask(ui::EventType type,
-                          const gfx::Point& host_location,
+                          const gfx::PointF& host_location,
                           int flags,
                           int changed_button_flags,
                           int64_t display_id,
@@ -333,7 +351,7 @@
   }
 
   void PostTouchEvent(ui::EventType type,
-                      const gfx::Point& host_location,
+                      const gfx::PointF& host_location,
                       int id,
                       int64_t display_id,
                       base::OnceClosure closure) {
@@ -344,14 +362,14 @@
   }
 
   void PostTouchEventTask(ui::EventType type,
-                          const gfx::Point& host_location,
+                          const gfx::PointF& host_location,
                           int id,
                           int64_t display_id,
                           base::OnceClosure closure) {
     ui::PointerDetails details(ui::EventPointerType::POINTER_TYPE_TOUCH, id,
                                1.0f, 1.0f, 0.0f);
-    ui::TouchEvent touch_event(type, host_location, ui::EventTimeForNow(),
-                               details);
+    ui::TouchEvent touch_event(type, host_location, host_location,
+                               ui::EventTimeForNow(), details);
     SendEventToSink(&touch_event, display_id, std::move(closure));
   }
 
@@ -367,19 +385,18 @@
         ->BindInterface(ws::mojom::kServiceName, &event_injector_);
   }
 
-  bool ScreenDIPToHostPixels(gfx::Point* location, int64_t* display_id) {
+  bool ScreenDIPToHostPixels(gfx::PointF* location, int64_t* display_id) {
     // The location needs to be in display's coordinate.
     display::Display display =
-        display::Screen::GetScreen()->GetDisplayNearestPoint(*location);
+        display::Screen::GetScreen()->GetDisplayNearestPoint(
+            gfx::ToFlooredPoint(*location));
     if (!display.is_valid()) {
       LOG(ERROR) << "Failed to find the display for " << location->ToString();
       return false;
     }
     *display_id = display.id();
     *location -= display.bounds().OffsetFromOrigin();
-    *location =
-        gfx::ScaleToFlooredPoint(*location, display.device_scale_factor(),
-                                 display.device_scale_factor());
+    location->Scale(display.device_scale_factor());
     return true;
   }
 
@@ -389,7 +406,7 @@
   // The mouse location for the last SendMouseEventsNotifyWhenDone call. This is
   // used rather than Env::last_mouse_location() as Env::last_mouse_location()
   // is updated asynchronously with mus.
-  base::Optional<gfx::Point> last_mouse_location_;
+  base::Optional<gfx::PointF> last_mouse_location_;
 
   // The display ID where the last SendMouseEventsNotifyWhenDone occurred. This
   // is used along with |last_mouse_location_| to send the mouse event to the
diff --git a/ui/compositor/layer.cc b/ui/compositor/layer.cc
index aa3d8b5..fe7f544 100644
--- a/ui/compositor/layer.cc
+++ b/ui/compositor/layer.cc
@@ -679,6 +679,7 @@
   cc_layer_->SetTransformOrigin(gfx::Point3F());
   cc_layer_->SetContentsOpaque(fills_bounds_opaquely_);
   cc_layer_->SetIsDrawable(type_ != LAYER_NOT_DRAWN);
+  cc_layer_->SetHitTestable(type_ != LAYER_NOT_DRAWN);
   cc_layer_->SetHideLayerAndSubtree(!visible_);
   cc_layer_->SetBackdropFilterQuality(backdrop_filter_quality_);
   cc_layer_->SetElementId(cc::ElementId(cc_layer_->id()));
@@ -1382,6 +1383,9 @@
   cc_layer_->SetContentsOpaque(true);
   cc_layer_->SetSafeOpaqueBackgroundColor(SK_ColorWHITE);
   cc_layer_->SetIsDrawable(type_ != LAYER_NOT_DRAWN);
+  // TODO(sunxd): Allow ui::Layers to set if they accept events or not. See
+  // https://crbug.com/924294.
+  cc_layer_->SetHitTestable(type_ != LAYER_NOT_DRAWN);
   cc_layer_->SetLayerClient(weak_ptr_factory_.GetWeakPtr());
   cc_layer_->SetElementId(cc::ElementId(cc_layer_->id()));
   RecomputePosition();
diff --git a/ui/display/manager/apply_content_protection_task.cc b/ui/display/manager/apply_content_protection_task.cc
index 7ffb5a934..b5e4389 100644
--- a/ui/display/manager/apply_content_protection_task.cc
+++ b/ui/display/manager/apply_content_protection_task.cc
@@ -4,8 +4,12 @@
 
 #include "ui/display/manager/apply_content_protection_task.h"
 
+#include <utility>
+#include <vector>
+
 #include "base/bind.h"
 #include "ui/display/manager/display_layout_manager.h"
+#include "ui/display/manager/display_util.h"
 #include "ui/display/types/display_snapshot.h"
 #include "ui/display/types/native_display_delegate.h"
 
@@ -17,24 +21,12 @@
     const DisplayLayoutManager& layout_manager,
     std::vector<DisplaySnapshot*>* hdcp_capable_displays) {
   for (DisplaySnapshot* display : layout_manager.GetDisplayStates()) {
-    switch (display->type()) {
-      case DISPLAY_CONNECTION_TYPE_UNKNOWN:
-        return false;
-      // DisplayPort, DVI, and HDMI all support HDCP.
-      case DISPLAY_CONNECTION_TYPE_DISPLAYPORT:
-      case DISPLAY_CONNECTION_TYPE_DVI:
-      case DISPLAY_CONNECTION_TYPE_HDMI:
-        hdcp_capable_displays->push_back(display);
-        break;
-      case DISPLAY_CONNECTION_TYPE_INTERNAL:
-      case DISPLAY_CONNECTION_TYPE_VGA:
-      case DISPLAY_CONNECTION_TYPE_NETWORK:
-        // No protections for these types. Do nothing.
-        break;
-      case DISPLAY_CONNECTION_TYPE_NONE:
-        NOTREACHED();
-        break;
-    }
+    uint32_t protection_mask;
+    if (!GetContentProtectionMethods(display->type(), &protection_mask))
+      return false;
+
+    if (protection_mask & CONTENT_PROTECTION_METHOD_HDCP)
+      hdcp_capable_displays->push_back(display);
   }
 
   return true;
@@ -45,28 +37,25 @@
 ApplyContentProtectionTask::ApplyContentProtectionTask(
     DisplayLayoutManager* layout_manager,
     NativeDisplayDelegate* native_display_delegate,
-    const DisplayConfigurator::ContentProtections& requests,
-    const ResponseCallback& callback)
+    DisplayConfigurator::ContentProtections requests,
+    ResponseCallback callback)
     : layout_manager_(layout_manager),
       native_display_delegate_(native_display_delegate),
-      requests_(requests),
-      callback_(callback),
-      query_status_(true),
-      pending_requests_(0),
-      weak_ptr_factory_(this) {}
+      requests_(std::move(requests)),
+      callback_(std::move(callback)) {}
 
 ApplyContentProtectionTask::~ApplyContentProtectionTask() {}
 
 void ApplyContentProtectionTask::Run() {
   std::vector<DisplaySnapshot*> hdcp_capable_displays;
   if (!GetHDCPCapableDisplays(*layout_manager_, &hdcp_capable_displays)) {
-    callback_.Run(false);
+    std::move(callback_).Run(/*success=*/false);
     return;
   }
 
   pending_requests_ = hdcp_capable_displays.size();
   if (pending_requests_ == 0) {
-    callback_.Run(true);
+    std::move(callback_).Run(/*success=*/true);
     return;
   }
 
@@ -75,24 +64,24 @@
   for (DisplaySnapshot* display : hdcp_capable_displays) {
     native_display_delegate_->GetHDCPState(
         *display,
-        base::Bind(&ApplyContentProtectionTask::OnHDCPStateUpdate,
-                   weak_ptr_factory_.GetWeakPtr(), display->display_id()));
+        base::BindOnce(&ApplyContentProtectionTask::OnGetHDCPState,
+                       weak_ptr_factory_.GetWeakPtr(), display->display_id()));
   }
 }
 
-void ApplyContentProtectionTask::OnHDCPStateUpdate(int64_t display_id,
-                                                   bool success,
-                                                   HDCPState state) {
-  query_status_ &= success;
-  display_hdcp_state_map_[display_id] = state;
+void ApplyContentProtectionTask::OnGetHDCPState(int64_t display_id,
+                                                bool success,
+                                                HDCPState state) {
+  success_ &= success;
+  hdcp_states_[display_id] = state;
   pending_requests_--;
 
   // Wait for all the requests before continuing.
   if (pending_requests_ != 0)
     return;
 
-  if (!query_status_) {
-    callback_.Run(false);
+  if (!success_) {
+    std::move(callback_).Run(/*success=*/false);
     return;
   }
 
@@ -102,7 +91,7 @@
 void ApplyContentProtectionTask::ApplyProtections() {
   std::vector<DisplaySnapshot*> hdcp_capable_displays;
   if (!GetHDCPCapableDisplays(*layout_manager_, &hdcp_capable_displays)) {
-    callback_.Run(false);
+    std::move(callback_).Run(/*success=*/false);
     return;
   }
 
@@ -111,18 +100,18 @@
   for (DisplaySnapshot* display : hdcp_capable_displays) {
     uint32_t desired_mask = GetDesiredProtectionMask(display->display_id());
 
-    auto it = display_hdcp_state_map_.find(display->display_id());
+    auto it = hdcp_states_.find(display->display_id());
     // If the display can't be found, the display configuration changed.
-    if (it == display_hdcp_state_map_.end()) {
-      callback_.Run(false);
+    if (it == hdcp_states_.end()) {
+      std::move(callback_).Run(/*success=*/false);
       return;
     }
 
     bool hdcp_enabled = it->second != HDCP_STATE_UNDESIRED;
     bool hdcp_requested = desired_mask & CONTENT_PROTECTION_METHOD_HDCP;
     if (hdcp_enabled != hdcp_requested) {
-      hdcp_requests.push_back(std::make_pair(
-          display, hdcp_requested ? HDCP_STATE_DESIRED : HDCP_STATE_UNDESIRED));
+      hdcp_requests.emplace_back(
+          display, hdcp_requested ? HDCP_STATE_DESIRED : HDCP_STATE_UNDESIRED);
     }
   }
 
@@ -130,24 +119,24 @@
   // All the requested changes are the same as the current HDCP state. Nothing
   // to do anymore, just ack the content protection change.
   if (pending_requests_ == 0) {
-    callback_.Run(true);
+    std::move(callback_).Run(/*success=*/true);
     return;
   }
 
   for (const auto& pair : hdcp_requests) {
     native_display_delegate_->SetHDCPState(
         *pair.first, pair.second,
-        base::Bind(&ApplyContentProtectionTask::OnHDCPStateApplied,
-                   weak_ptr_factory_.GetWeakPtr()));
+        base::BindOnce(&ApplyContentProtectionTask::OnSetHDCPState,
+                       weak_ptr_factory_.GetWeakPtr()));
   }
 }
 
-void ApplyContentProtectionTask::OnHDCPStateApplied(bool success) {
-  query_status_ &= success;
+void ApplyContentProtectionTask::OnSetHDCPState(bool success) {
+  success_ &= success;
   pending_requests_--;
 
   if (pending_requests_ == 0)
-    callback_.Run(query_status_);
+    std::move(callback_).Run(success_);
 }
 
 uint32_t ApplyContentProtectionTask::GetDesiredProtectionMask(
@@ -157,7 +146,7 @@
   // In non-mirror mode, only request of client's display needs to be
   // fulfilled.
   if (layout_manager_->IsMirroring()) {
-    for (auto pair : requests_)
+    for (const auto& pair : requests_)
       desired_mask |= pair.second;
   } else {
     auto it = requests_.find(display_id);
diff --git a/ui/display/manager/apply_content_protection_task.h b/ui/display/manager/apply_content_protection_task.h
index 2d8610f..be1d902 100644
--- a/ui/display/manager/apply_content_protection_task.h
+++ b/ui/display/manager/apply_content_protection_task.h
@@ -5,12 +5,10 @@
 #ifndef UI_DISPLAY_MANAGER_APPLY_CONTENT_PROTECTION_TASK_H_
 #define UI_DISPLAY_MANAGER_APPLY_CONTENT_PROTECTION_TASK_H_
 
-#include <stddef.h>
-#include <stdint.h>
+#include <cstddef>
+#include <cstdint>
 
-#include <map>
-#include <vector>
-
+#include "base/containers/flat_map.h"
 #include "base/macros.h"
 #include "base/memory/weak_ptr.h"
 #include "ui/display/manager/display_configurator.h"
@@ -24,61 +22,47 @@
 // manner:
 // 1) Run()
 //   a) Query NativeDisplayDelegate for HDCP state on capable displays
-//   b) OnHDCPStateUpdate() called for each display as response to (a)
+//   b) OnGetHDCPState() called for each display as response to (a)
 // 2) ApplyProtections()
 //   a) Compute preferred HDCP state for capable displays
 //   b) Call into NativeDisplayDelegate to set HDCP state on capable displays
-//   c) OnHDCPStateApplied() called for each display as reponse to (b)
+//   c) OnSetHDCPState() called for each display as response to (b)
 // 3) Call |callback_| to signal end of task.
 //
 // Note, in steps 1a and 2a, if no HDCP capable displays are found or if errors
 // are reported, the task finishes early and skips to step 3.
 class DISPLAY_MANAGER_EXPORT ApplyContentProtectionTask {
  public:
-  typedef base::Callback<void(bool)> ResponseCallback;
+  using ResponseCallback = base::OnceCallback<void(bool success)>;
 
-  ApplyContentProtectionTask(
-      DisplayLayoutManager* layout_manager,
-      NativeDisplayDelegate* native_display_delegate,
-      const DisplayConfigurator::ContentProtections& requests,
-      const ResponseCallback& callback);
+  ApplyContentProtectionTask(DisplayLayoutManager* layout_manager,
+                             NativeDisplayDelegate* native_display_delegate,
+                             DisplayConfigurator::ContentProtections requests,
+                             ResponseCallback callback);
   ~ApplyContentProtectionTask();
 
   void Run();
 
  private:
-  // Callback to NatvieDisplayDelegate::GetHDCPState()
-  void OnHDCPStateUpdate(int64_t display_id, bool success, HDCPState state);
-
-  // Callback to NativeDisplayDelegate::SetHDCPState()
-  void OnHDCPStateApplied(bool success);
+  void OnGetHDCPState(int64_t display_id, bool success, HDCPState state);
+  void OnSetHDCPState(bool success);
 
   void ApplyProtections();
 
   uint32_t GetDesiredProtectionMask(int64_t display_id) const;
 
-  DisplayLayoutManager* layout_manager_;  // Not owned.
+  DisplayLayoutManager* const layout_manager_;            // Not owned.
+  NativeDisplayDelegate* const native_display_delegate_;  // Not owned.
 
-  NativeDisplayDelegate* native_display_delegate_;  // Not owned.
-
-  DisplayConfigurator::ContentProtections requests_;
-
-  // Callback used to respond once the task finishes.
+  const DisplayConfigurator::ContentProtections requests_;
   ResponseCallback callback_;
 
-  // Mapping between display IDs and the HDCP state returned by
-  // NativeDisplayDelegate.
-  std::map<int64_t, HDCPState> display_hdcp_state_map_;
+  base::flat_map<int64_t /* display_id */, HDCPState> hdcp_states_;
 
-  // Tracks the status of the NativeDisplayDelegate responses. This will be true
-  // if all the queries were successful, false otherwise.
-  bool query_status_;
+  bool success_ = true;
+  size_t pending_requests_ = 0;
 
-  // Tracks the number of NativeDisplayDelegate requests sent but not answered
-  // yet.
-  size_t pending_requests_;
-
-  base::WeakPtrFactory<ApplyContentProtectionTask> weak_ptr_factory_;
+  base::WeakPtrFactory<ApplyContentProtectionTask> weak_ptr_factory_{this};
 
   DISALLOW_COPY_AND_ASSIGN(ApplyContentProtectionTask);
 };
diff --git a/ui/display/manager/apply_content_protection_task_unittest.cc b/ui/display/manager/apply_content_protection_task_unittest.cc
index e63e244..294b6ea 100644
--- a/ui/display/manager/apply_content_protection_task_unittest.cc
+++ b/ui/display/manager/apply_content_protection_task_unittest.cc
@@ -37,28 +37,29 @@
 
 class ApplyContentProtectionTaskTest : public testing::Test {
  public:
-  enum Response {
+  enum class Response {
     ERROR,
     SUCCESS,
     NOT_CALLED,
   };
 
-  ApplyContentProtectionTaskTest()
-      : response_(NOT_CALLED), display_delegate_(&log_) {}
-  ~ApplyContentProtectionTaskTest() override {}
+  ApplyContentProtectionTaskTest() = default;
+  ~ApplyContentProtectionTaskTest() override = default;
 
-  void ResponseCallback(bool success) { response_ = success ? SUCCESS : ERROR; }
+  void ResponseCallback(bool success) {
+    response_ = success ? Response::SUCCESS : Response::ERROR;
+  }
 
  protected:
-  Response response_;
+  Response response_ = Response::NOT_CALLED;
   ActionLogger log_;
-  TestNativeDisplayDelegate display_delegate_;
+  TestNativeDisplayDelegate display_delegate_{&log_};
 
  private:
   DISALLOW_COPY_AND_ASSIGN(ApplyContentProtectionTaskTest);
 };
 
-TEST_F(ApplyContentProtectionTaskTest, ApplyWithNoHDCPCapableDisplay) {
+TEST_F(ApplyContentProtectionTaskTest, ApplyHdcpToInternalDisplay) {
   std::vector<std::unique_ptr<DisplaySnapshot>> displays;
   displays.push_back(
       CreateDisplaySnapshot(1, DISPLAY_CONNECTION_TYPE_INTERNAL));
@@ -73,11 +74,11 @@
                  base::Unretained(this)));
   task.Run();
 
-  EXPECT_EQ(SUCCESS, response_);
+  EXPECT_EQ(Response::SUCCESS, response_);
   EXPECT_EQ(kNoActions, log_.GetActionsAndClear());
 }
 
-TEST_F(ApplyContentProtectionTaskTest, ApplyWithHDMIDisplay) {
+TEST_F(ApplyContentProtectionTaskTest, ApplyHdcpToExternalDisplay) {
   std::vector<std::unique_ptr<DisplaySnapshot>> displays;
   displays.push_back(CreateDisplaySnapshot(1, DISPLAY_CONNECTION_TYPE_HDMI));
   TestDisplayLayoutManager layout_manager(std::move(displays),
@@ -91,7 +92,7 @@
                  base::Unretained(this)));
   task.Run();
 
-  EXPECT_EQ(SUCCESS, response_);
+  EXPECT_EQ(Response::SUCCESS, response_);
   EXPECT_EQ(
       JoinActions(GetSetHDCPStateAction(*layout_manager.GetDisplayStates()[0],
                                         HDCP_STATE_DESIRED)
@@ -100,7 +101,7 @@
       log_.GetActionsAndClear());
 }
 
-TEST_F(ApplyContentProtectionTaskTest, ApplyWithUnknownDisplay) {
+TEST_F(ApplyContentProtectionTaskTest, ApplyHdcpToUnknownDisplay) {
   std::vector<std::unique_ptr<DisplaySnapshot>> displays;
   displays.push_back(CreateDisplaySnapshot(1, DISPLAY_CONNECTION_TYPE_UNKNOWN));
   TestDisplayLayoutManager layout_manager(std::move(displays),
@@ -114,11 +115,11 @@
                  base::Unretained(this)));
   task.Run();
 
-  EXPECT_EQ(ERROR, response_);
+  EXPECT_EQ(Response::ERROR, response_);
   EXPECT_EQ(kNoActions, log_.GetActionsAndClear());
 }
 
-TEST_F(ApplyContentProtectionTaskTest, FailGettingHDCPState) {
+TEST_F(ApplyContentProtectionTaskTest, ApplyHdcpToDisplayThatCannotGetHdcp) {
   std::vector<std::unique_ptr<DisplaySnapshot>> displays;
   displays.push_back(CreateDisplaySnapshot(1, DISPLAY_CONNECTION_TYPE_HDMI));
   TestDisplayLayoutManager layout_manager(std::move(displays),
@@ -133,11 +134,11 @@
                  base::Unretained(this)));
   task.Run();
 
-  EXPECT_EQ(ERROR, response_);
+  EXPECT_EQ(Response::ERROR, response_);
   EXPECT_EQ(kNoActions, log_.GetActionsAndClear());
 }
 
-TEST_F(ApplyContentProtectionTaskTest, FailSettingHDCPState) {
+TEST_F(ApplyContentProtectionTaskTest, ApplyHdcpToDisplayThatCannotSetHdcp) {
   std::vector<std::unique_ptr<DisplaySnapshot>> displays;
   displays.push_back(CreateDisplaySnapshot(1, DISPLAY_CONNECTION_TYPE_HDMI));
   TestDisplayLayoutManager layout_manager(std::move(displays),
@@ -152,7 +153,7 @@
                  base::Unretained(this)));
   task.Run();
 
-  EXPECT_EQ(ERROR, response_);
+  EXPECT_EQ(Response::ERROR, response_);
   EXPECT_EQ(
       JoinActions(GetSetHDCPStateAction(*layout_manager.GetDisplayStates()[0],
                                         HDCP_STATE_DESIRED)
@@ -161,7 +162,7 @@
       log_.GetActionsAndClear());
 }
 
-TEST_F(ApplyContentProtectionTaskTest, ApplyNoopProtection) {
+TEST_F(ApplyContentProtectionTaskTest, ApplyNoProtectionToExternalDisplay) {
   std::vector<std::unique_ptr<DisplaySnapshot>> displays;
   displays.push_back(CreateDisplaySnapshot(1, DISPLAY_CONNECTION_TYPE_HDMI));
   TestDisplayLayoutManager layout_manager(std::move(displays),
@@ -176,7 +177,7 @@
                  base::Unretained(this)));
   task.Run();
 
-  EXPECT_EQ(SUCCESS, response_);
+  EXPECT_EQ(Response::SUCCESS, response_);
   EXPECT_EQ(kNoActions, log_.GetActionsAndClear());
 }
 
diff --git a/ui/events/blink/event_with_callback.h b/ui/events/blink/event_with_callback.h
index 1032ad1b..a554275 100644
--- a/ui/events/blink/event_with_callback.h
+++ b/ui/events/blink/event_with_callback.h
@@ -50,7 +50,8 @@
 
   const blink::WebInputEvent& event() const { return *event_; }
   blink::WebInputEvent* event_pointer() { return event_.get(); }
-  const LatencyInfo latency_info() const { return latency_; }
+  const LatencyInfo& latency_info() const { return latency_; }
+  LatencyInfo* mutable_latency_info() { return &latency_; }
   base::TimeTicks creation_timestamp() const { return creation_timestamp_; }
   base::TimeTicks last_coalesced_timestamp() const {
     return last_coalesced_timestamp_;
diff --git a/ui/events/blink/input_handler_proxy.cc b/ui/events/blink/input_handler_proxy.cc
index 696adc7..8aceebd 100644
--- a/ui/events/blink/input_handler_proxy.cc
+++ b/ui/events/blink/input_handler_proxy.cc
@@ -263,7 +263,6 @@
     std::unique_ptr<EventWithCallback> event_with_callback,
     const base::TimeTicks now) {
   ui::LatencyInfo monitored_latency_info = event_with_callback->latency_info();
-
   std::unique_ptr<cc::SwapPromiseMonitor> latency_info_swap_promise_monitor =
       input_handler_->CreateLatencyInfoSwapPromiseMonitor(
           &monitored_latency_info);
@@ -301,12 +300,8 @@
   base::TimeTicks now = tick_clock_->NowTicks();
   while (!compositor_event_queue_->empty()) {
     std::unique_ptr<EventWithCallback> event_with_callback =
-        compositor_event_queue_->Pop();
-    if (scroll_predictor_) {
-      scroll_predictor_->ResampleScrollEvents(
-          event_with_callback->original_events(), now,
-          event_with_callback->event_pointer());
-    }
+        scroll_predictor_->ResampleScrollEvents(compositor_event_queue_->Pop(),
+                                                now);
 
     DispatchSingleInputEvent(std::move(event_with_callback), now);
   }
diff --git a/ui/events/blink/scroll_predictor.cc b/ui/events/blink/scroll_predictor.cc
index ab396ffd..a6cca97 100644
--- a/ui/events/blink/scroll_predictor.cc
+++ b/ui/events/blink/scroll_predictor.cc
@@ -55,20 +55,23 @@
   }
 }
 
-void ScrollPredictor::ResampleScrollEvents(
-    const EventWithCallback::OriginalEventList& original_events,
-    base::TimeTicks frame_time,
-    WebInputEvent* event) {
+std::unique_ptr<EventWithCallback> ScrollPredictor::ResampleScrollEvents(
+    std::unique_ptr<EventWithCallback> event_with_callback,
+    base::TimeTicks frame_time) {
   if (!should_resample_scroll_events_)
-    return;
+    return event_with_callback;
 
-  if (event->GetType() == WebInputEvent::kGestureScrollUpdate) {
+  const EventWithCallback::OriginalEventList& original_events =
+      event_with_callback->original_events();
+
+  if (event_with_callback->event().GetType() ==
+      WebInputEvent::kGestureScrollUpdate) {
     // TODO(eirage): When scroll events are coalesced with pinch, we can have
     // empty original event list. In that case, we can't use the original events
     // to update the prediction. We don't want to use the aggregated event to
     // update because of the event time stamp, so skip the prediction for now.
     if (original_events.empty())
-      return;
+      return event_with_callback;
 
     TRACE_EVENT_BEGIN0("input", "ScrollPredictor::ResampleScrollEvents");
 
@@ -80,14 +83,18 @@
       UpdatePrediction(coalesced_event.event_, frame_time);
 
     if (enable_resampling_ && should_resample_scroll_events_)
-      ResampleEvent(frame_time, event);
+      ResampleEvent(frame_time, event_with_callback->event_pointer(),
+                    event_with_callback->mutable_latency_info());
 
     TRACE_EVENT_END2("input", "ScrollPredictor::ResampleScrollEvents",
                      "OriginalPosition", current_accumulated_delta_.ToString(),
                      "PredictedPosition", last_accumulated_delta_.ToString());
-  } else if (event->GetType() == WebInputEvent::kGestureScrollEnd) {
+  } else if (event_with_callback->event().GetType() ==
+             WebInputEvent::kGestureScrollEnd) {
     should_resample_scroll_events_ = false;
   }
+
+  return event_with_callback;
 }
 
 void ScrollPredictor::Reset() {
@@ -118,7 +125,8 @@
 }
 
 void ScrollPredictor::ResampleEvent(base::TimeTicks time_stamp,
-                                    WebInputEvent* event) {
+                                    WebInputEvent* event,
+                                    LatencyInfo* latency_info) {
   DCHECK(event->GetType() == WebInputEvent::kGestureScrollUpdate);
   WebGestureEvent* gesture_event = static_cast<WebGestureEvent*>(event);
 
@@ -145,6 +153,8 @@
       (new_delta.y() * gesture_event->data.scroll_update.delta_y < 0)
           ? 0
           : new_delta.y();
+  // Sync the predicted delta_y to latency_info for AverageLag metric.
+  latency_info->set_predicted_scroll_update_delta(new_delta.y());
 
   last_accumulated_delta_.Offset(gesture_event->data.scroll_update.delta_x,
                                  gesture_event->data.scroll_update.delta_y);
diff --git a/ui/events/blink/scroll_predictor.h b/ui/events/blink/scroll_predictor.h
index e9d7d2b..2ccc44c 100644
--- a/ui/events/blink/scroll_predictor.h
+++ b/ui/events/blink/scroll_predictor.h
@@ -35,10 +35,9 @@
   // Resampling GestureScrollUpdate events. Updates the prediction with events
   // in original events list, and apply the prediction to the aggregated GSU
   // event if enable_resampling is true.
-  void ResampleScrollEvents(
-      const EventWithCallback::OriginalEventList& original_events,
-      base::TimeTicks frame_time,
-      blink::WebInputEvent* event);
+  std::unique_ptr<EventWithCallback> ResampleScrollEvents(
+      std::unique_ptr<EventWithCallback> event_with_callback,
+      base::TimeTicks frame_time);
 
  private:
   friend class test::InputHandlerProxyEventQueueTest;
@@ -53,7 +52,9 @@
                         base::TimeTicks frame_time);
 
   // Apply resampled deltaX/deltaY to gesture events
-  void ResampleEvent(base::TimeTicks frame_time, blink::WebInputEvent* event);
+  void ResampleEvent(base::TimeTicks frame_time,
+                     blink::WebInputEvent* event,
+                     LatencyInfo* latency_info);
 
   // Reports prediction accuracy UMA histogram. Calculates position in current
   // event time and compute the distance between real event and predicted event.
diff --git a/ui/events/blink/scroll_predictor_unittest.cc b/ui/events/blink/scroll_predictor_unittest.cc
index 597c5bd..588c2264 100644
--- a/ui/events/blink/scroll_predictor_unittest.cc
+++ b/ui/events/blink/scroll_predictor_unittest.cc
@@ -78,12 +78,18 @@
 
   void HandleResampleScrollEvents(WebScopedInputEvent& event,
                                   double time_delta_in_milliseconds = 0) {
-    scroll_predictor_->ResampleScrollEvents(
-        original_events_,
+    std::unique_ptr<EventWithCallback> event_with_callback =
+        std::make_unique<EventWithCallback>(std::move(event), LatencyInfo(),
+                                            base::TimeTicks(),
+                                            base::NullCallback());
+    event_with_callback->original_events() = std::move(original_events_);
+
+    event_with_callback = scroll_predictor_->ResampleScrollEvents(
+        std::move(event_with_callback),
         WebInputEvent::GetStaticTimeStampForTests() +
-            base::TimeDelta::FromMillisecondsD(time_delta_in_milliseconds),
-        event.get());
-    original_events_.clear();
+            base::TimeDelta::FromMillisecondsD(time_delta_in_milliseconds));
+
+    event = WebInputEventTraits::Clone(event_with_callback->event());
   }
 
   bool PredictionAvailable(ui::InputPredictor::InputData* result,
diff --git a/ui/gfx/platform_font_win.h b/ui/gfx/platform_font_win.h
index e95423b..61496a3 100644
--- a/ui/gfx/platform_font_win.h
+++ b/ui/gfx/platform_font_win.h
@@ -63,7 +63,6 @@
   static bool IsDirectWriteEnabled();
 
  private:
-  FRIEND_TEST_ALL_PREFIXES(RenderTextTest, HarfBuzz_UniscribeFallback);
   FRIEND_TEST_ALL_PREFIXES(PlatformFontWinTest, Metrics_SkiaVersusGDI);
   FRIEND_TEST_ALL_PREFIXES(PlatformFontWinTest, DirectWriteFontSubstitution);
 
@@ -112,7 +111,6 @@
 
    private:
     friend class base::RefCounted<HFontRef>;
-    FRIEND_TEST_ALL_PREFIXES(RenderTextTest, HarfBuzz_UniscribeFallback);
     FRIEND_TEST_ALL_PREFIXES(PlatformFontWinTest, Metrics_SkiaVersusGDI);
     FRIEND_TEST_ALL_PREFIXES(PlatformFontWinTest, DirectWriteFontSubstitution);
 
diff --git a/ui/gfx/render_text_unittest.cc b/ui/gfx/render_text_unittest.cc
index e1b63b1..bdb72e6 100644
--- a/ui/gfx/render_text_unittest.cc
+++ b/ui/gfx/render_text_unittest.cc
@@ -44,7 +44,6 @@
 
 #if defined(OS_WIN)
 #include "base/win/windows_version.h"
-#include "ui/gfx/platform_font_win.h"
 #endif
 
 #if defined(OS_MACOSX)
@@ -4189,12 +4188,35 @@
 // Ensure that the fallback fonts of the Uniscribe font are tried for shaping.
 #if defined(OS_WIN)
 TEST_F(RenderTextTest, HarfBuzz_UniscribeFallback) {
+  std::string font_name;
+  std::string localized_font_name;
+
+  base::win::Version version = base::win::GetVersion();
+  if (version < base::win::VERSION_WIN10) {
+    // The font 'Meiryo' exists on windows 7 and windows 8. see:
+    // https://docs.microsoft.com/en-us/typography/fonts/windows_7_font_list
+    // https://docs.microsoft.com/en-us/typography/fonts/windows_8_font_list
+    font_name = "Meiryo";
+    // Japanese name for Meiryo.
+    localized_font_name = "\u30e1\u30a4\u30ea\u30aa";
+  } else {
+    ASSERT_GE(version, base::win::VERSION_WIN10);
+    // The font 'Malgun Gothic' exists on windows 10. see:
+    // https://docs.microsoft.com/en-us/typography/fonts/windows_10_font_list
+    font_name = "Malgun Gothic";
+    // Korean name for Malgun Gothic.
+    localized_font_name = "\ub9d1\uc740 \uace0\ub515";
+  }
+
+  // The localized name won't be found in the system's linked fonts, forcing
+  // RTHB to try the Uniscribe font and its fallbacks.
   RenderTextHarfBuzz* render_text = GetRenderText();
-  PlatformFontWin* font_win = new PlatformFontWin("Meiryo", 12);
-  // Japanese name for Meiryo. This name won't be found in the system's linked
-  // fonts, forcing RTHB to try the Uniscribe font and its fallbacks.
-  font_win->font_ref_->font_name_ = "\u30e1\u30a4\u30ea\u30aa";
-  FontList font_list((Font(font_win)));
+  Font font(localized_font_name, 12);
+  FontList font_list(font);
+
+  // Ensures the font didn't got substituted.
+  EXPECT_NE(font.GetFontName(), font_name);
+  EXPECT_EQ(font.GetActualFontNameForTesting(), localized_font_name);
 
   render_text->SetFontList(font_list);
   // An invalid Unicode character that somehow yields Korean character "han".
diff --git a/ui/latency/average_lag_tracker.cc b/ui/latency/average_lag_tracker.cc
index e1419ba..1cf21c8 100644
--- a/ui/latency/average_lag_tracker.cc
+++ b/ui/latency/average_lag_tracker.cc
@@ -90,7 +90,7 @@
 
   last_event_timestamp_ = event_timestamp;
   last_event_accumulated_delta_ += latency.scroll_update_delta();
-  last_rendered_accumulated_delta_ += latency.scroll_update_delta();
+  last_rendered_accumulated_delta_ += latency.predicted_scroll_update_delta();
 }
 
 float AverageLagTracker::LagBetween(base::TimeTicks front_time,
diff --git a/ui/latency/average_lag_tracker_unittest.cc b/ui/latency/average_lag_tracker_unittest.cc
index c4c34f1..a684c45 100644
--- a/ui/latency/average_lag_tracker_unittest.cc
+++ b/ui/latency/average_lag_tracker_unittest.cc
@@ -30,9 +30,13 @@
 
   void SyntheticTouchScrollBeginLatencyInfo(base::TimeTicks event_time,
                                             base::TimeTicks frame_time,
-                                            float delta) {
+                                            float delta,
+                                            float predicted_delta = 0) {
     ui::LatencyInfo touch_latency(ui::SourceEventType::TOUCH);
     touch_latency.set_scroll_update_delta(delta);
+    touch_latency.set_predicted_scroll_update_delta(
+        predicted_delta != 0 ? predicted_delta : delta);
+
     touch_latency.AddLatencyNumberWithTimestamp(
         ui::INPUT_EVENT_LATENCY_FIRST_SCROLL_UPDATE_ORIGINAL_COMPONENT,
         event_time, 1);
@@ -45,9 +49,12 @@
 
   void SyntheticTouchScrollUpdateLatencyInfo(base::TimeTicks event_time,
                                              base::TimeTicks frame_time,
-                                             float delta) {
+                                             float delta,
+                                             float predicted_delta = 0) {
     ui::LatencyInfo touch_latency(ui::SourceEventType::TOUCH);
     touch_latency.set_scroll_update_delta(delta);
+    touch_latency.set_predicted_scroll_update_delta(
+        predicted_delta != 0 ? predicted_delta : delta);
     touch_latency.AddLatencyNumberWithTimestamp(
         ui::INPUT_EVENT_LATENCY_SCROLL_UPDATE_ORIGINAL_COMPONENT, event_time,
         1);
@@ -244,5 +251,125 @@
               ElementsAre(Bucket(12, 1)));
 }
 
+// A simple case without scroll prediction to compare with the two with
+// prediction cases below.
+TEST_F(AverageLagTrackerTest, NoScrollPrediction) {
+  // ScrollBegin, at t=5, finter_pos=5px.
+  base::TimeTicks event_time = MillisecondsToTimeTicks(5);
+  base::TimeTicks frame_time = MillisecondsToTimeTicks(10);
+  SyntheticTouchScrollBeginLatencyInfo(event_time, frame_time,
+                                       5 /* scroll_delta */);
+
+  // ScrollUpdate, at t=15, finger_pos=15px.
+  event_time = MillisecondsToTimeTicks(15);
+  frame_time = MillisecondsToTimeTicks(20);
+  SyntheticTouchScrollUpdateLatencyInfo(event_time, frame_time,
+                                        10 /* scroll_delta */);
+
+  // ScrollUpdate, at t=25, finger_pos=25px.
+  event_time = MillisecondsToTimeTicks(25);
+  frame_time = MillisecondsToTimeTicks(30);
+  SyntheticTouchScrollUpdateLatencyInfo(event_time, frame_time,
+                                        10 /* scroll_delta */);
+
+  // Another ScrollBegin to flush unfinished frames.
+  event_time = MillisecondsToTimeTicks(1000);
+  frame_time = MillisecondsToTimeTicks(1000);
+  SyntheticTouchScrollBeginLatencyInfo(event_time, frame_time, 0);
+
+  // Prediction hasn't take affect on ScrollBegin so it'll stay the same.
+  EXPECT_THAT(histogram_tester().GetAllSamples(
+                  "Event.Latency.ScrollBegin.Touch.AverageLag"),
+              ElementsAre(Bucket(7, 1)));
+  // At t=10, finger_pos = 10px, rendered_pos = 5px.
+  // At t=20, finger_pos = 20px, rendered_pos = 15px.
+  // At t=30, finger_pos = 25px, rendered_pos = 25px.
+  // AverageLag = ((5px+15px)*10ms/2 + (5px+10px)*5ms/2 + 10px*5ms)/20ms
+  //            = 9.375
+  EXPECT_THAT(histogram_tester().GetAllSamples(
+                  "Event.Latency.ScrollUpdate.Touch.AverageLag"),
+              ElementsAre(Bucket(9, 1)));
+}
+
+// Test AverageLag with perfect scroll prediction.
+TEST_F(AverageLagTrackerTest, ScrollPrediction) {
+  // ScrollBegin, at t=5, finter_pos=5px.
+  // Predict frame_time=10, predicted_pos = 10px.
+  base::TimeTicks event_time = MillisecondsToTimeTicks(5);
+  base::TimeTicks frame_time = MillisecondsToTimeTicks(10);
+  SyntheticTouchScrollBeginLatencyInfo(
+      event_time, frame_time, 5 /* scroll_delta */, 10 /* predicted_delta */);
+
+  // ScrollUpdate, at t=15, finger_pos=15px.
+  // Predict frame_time=20, predicted_pos = 20px.
+  event_time = MillisecondsToTimeTicks(15);
+  frame_time = MillisecondsToTimeTicks(20);
+  SyntheticTouchScrollUpdateLatencyInfo(
+      event_time, frame_time, 10 /* scroll_delta */, 10 /* predicted_delta */);
+
+  // ScrollUpdate, at t=25, finger_pos=25px.
+  // Predict frame_time=30, predicted_pos = 30px.
+  event_time = MillisecondsToTimeTicks(25);
+  frame_time = MillisecondsToTimeTicks(30);
+  SyntheticTouchScrollUpdateLatencyInfo(
+      event_time, frame_time, 10 /* scroll_delta */, 10 /* predicted_delta */);
+
+  // Another ScrollBegin to flush unfinished frames.
+  event_time = MillisecondsToTimeTicks(1000);
+  frame_time = MillisecondsToTimeTicks(1000);
+  SyntheticTouchScrollBeginLatencyInfo(event_time, frame_time, 0);
+
+  // Prediction hasn't take affect on ScrollBegin so it'll stay the same.
+  EXPECT_THAT(histogram_tester().GetAllSamples(
+                  "Event.Latency.ScrollBegin.Touch.AverageLag"),
+              ElementsAre(Bucket(7, 1)));
+  // At t=10, finger_pos = 10px, rendered_pos = 10px.
+  // At t=20, finger_pos = 20px, rendered_pos = 20px.
+  // At t=30, finger_pos = 25px, rendered_pos = 30px.
+  // AverageLag = ((0px+10px)*10ms/2 + (0px+5px)*10ms/2 + 5px*5ms)/20ms
+  //            = 4.375px
+  EXPECT_THAT(histogram_tester().GetAllSamples(
+                  "Event.Latency.ScrollUpdate.Touch.AverageLag"),
+              ElementsAre(Bucket(4, 1)));
+}
+
+// Test AverageLag with imperfect scroll prediction.
+TEST_F(AverageLagTrackerTest, ImperfectScrollPrediction) {
+  // ScrollBegin, at t=5, finter_pos=5px.
+  // Predict frame_time=10, predicted_pos(over) = 12px.
+  base::TimeTicks event_time = MillisecondsToTimeTicks(5);
+  base::TimeTicks frame_time = MillisecondsToTimeTicks(10);
+  SyntheticTouchScrollBeginLatencyInfo(
+      event_time, frame_time, 5 /* scroll_delta */, 12 /* predicted_delta */);
+
+  // ScrollUpdate, at t=15, finger_pos=15px.
+  // Predict frame_time=20, predicted_pos(under) = 17px.
+  event_time = MillisecondsToTimeTicks(15);
+  frame_time = MillisecondsToTimeTicks(20);
+  SyntheticTouchScrollUpdateLatencyInfo(
+      event_time, frame_time, 10 /* scroll_delta */, 5 /* predicted_delta */);
+
+  // ScrollUpdate, at t=25, finger_pos=25px.
+  // Predict frame_time=30, predicted_pos(over) = 31px.
+  event_time = MillisecondsToTimeTicks(25);
+  frame_time = MillisecondsToTimeTicks(30);
+  SyntheticTouchScrollUpdateLatencyInfo(
+      event_time, frame_time, 10 /* scroll_delta */, 14 /* predicted_delta */);
+
+  // Another ScrollBegin to flush unfinished frames.
+  event_time = MillisecondsToTimeTicks(1000);
+  frame_time = MillisecondsToTimeTicks(1000);
+  SyntheticTouchScrollBeginLatencyInfo(event_time, frame_time, 0);
+
+  EXPECT_THAT(histogram_tester().GetAllSamples(
+                  "Event.Latency.ScrollBegin.Touch.AverageLag"),
+              ElementsAre(Bucket(7, 1)));
+  // AverageLag = ((2px*2ms/2+8px*8ms/2)+ ((3px+8px)*5ms/2+8px*5ms))/20ms
+  //            = 5.075px
+  EXPECT_THAT(histogram_tester().GetAllSamples(
+                  "Event.Latency.ScrollUpdate.Touch.AverageLag"),
+              ElementsAre(Bucket(5, 1)));
+}
+
 }  // namespace
 }  // namespace ui
diff --git a/ui/latency/ipc/latency_info_param_traits.cc b/ui/latency/ipc/latency_info_param_traits.cc
index 5aceb85f..d963612 100644
--- a/ui/latency/ipc/latency_info_param_traits.cc
+++ b/ui/latency/ipc/latency_info_param_traits.cc
@@ -41,6 +41,7 @@
   WriteParam(m, p.terminated_);
   WriteParam(m, p.source_event_type_);
   WriteParam(m, p.scroll_update_delta_);
+  WriteParam(m, p.predicted_scroll_update_delta_);
 }
 
 bool ParamTraits<ui::LatencyInfo>::Read(const base::Pickle* m,
@@ -65,6 +66,8 @@
     return false;
   if (!ReadParam(m, iter, &p->scroll_update_delta_))
     return false;
+  if (!ReadParam(m, iter, &p->predicted_scroll_update_delta_))
+    return false;
 
   return true;
 }
@@ -87,6 +90,8 @@
   LogParam(p.source_event_type_, l);
   l->append(" ");
   LogParam(p.scroll_update_delta_, l);
+  l->append(" ");
+  LogParam(p.predicted_scroll_update_delta_, l);
 }
 
 }  // namespace IPC
diff --git a/ui/latency/ipc/latency_info_param_traits_unittest.cc b/ui/latency/ipc/latency_info_param_traits_unittest.cc
index b31706c..b133ed4 100644
--- a/ui/latency/ipc/latency_info_param_traits_unittest.cc
+++ b/ui/latency/ipc/latency_info_param_traits_unittest.cc
@@ -18,6 +18,7 @@
   latency.set_trace_id(5);
   latency.set_ukm_source_id(10);
   latency.set_scroll_update_delta(12.5);
+  latency.set_predicted_scroll_update_delta(12.5);
   ASSERT_FALSE(latency.terminated());
   latency.AddLatencyNumber(INPUT_EVENT_LATENCY_ORIGINAL_COMPONENT);
   latency.AddLatencyNumber(INPUT_EVENT_LATENCY_BEGIN_RWH_COMPONENT);
@@ -37,6 +38,8 @@
   EXPECT_EQ(latency.ukm_source_id(), output.ukm_source_id());
   EXPECT_EQ(latency.terminated(), output.terminated());
   EXPECT_EQ(latency.scroll_update_delta(), output.scroll_update_delta());
+  EXPECT_EQ(latency.predicted_scroll_update_delta(),
+            output.predicted_scroll_update_delta());
 
   EXPECT_TRUE(output.FindLatency(INPUT_EVENT_LATENCY_ORIGINAL_COMPONENT,
                                  nullptr));
diff --git a/ui/latency/latency_info.cc b/ui/latency/latency_info.cc
index fd7e1fd..96f6660 100644
--- a/ui/latency/latency_info.cc
+++ b/ui/latency/latency_info.cc
@@ -126,7 +126,8 @@
       began_(false),
       terminated_(false),
       source_event_type_(type),
-      scroll_update_delta_(0) {}
+      scroll_update_delta_(0),
+      predicted_scroll_update_delta_(0) {}
 
 LatencyInfo::LatencyInfo(const LatencyInfo& other) = default;
 
@@ -139,7 +140,8 @@
       began_(false),
       terminated_(terminated),
       source_event_type_(SourceEventType::UNKNOWN),
-      scroll_update_delta_(0) {}
+      scroll_update_delta_(0),
+      predicted_scroll_update_delta_(0) {}
 
 bool LatencyInfo::Verify(const std::vector<LatencyInfo>& latency_info,
                          const char* referring_msg) {
@@ -325,6 +327,15 @@
   }
 
   scroll_update_delta_ += other.scroll_update_delta();
+  predicted_scroll_update_delta_ += other.predicted_scroll_update_delta();
+}
+
+LatencyInfo LatencyInfo::ScaledBy(float scale) const {
+  ui::LatencyInfo scaled_latency_info(*this);
+  scaled_latency_info.set_scroll_update_delta(scroll_update_delta_ * scale);
+  scaled_latency_info.set_predicted_scroll_update_delta(
+      predicted_scroll_update_delta_ * scale);
+  return scaled_latency_info;
 }
 
 std::unique_ptr<base::trace_event::ConvertableToTraceFormat>
diff --git a/ui/latency/latency_info.h b/ui/latency/latency_info.h
index 91b3f91c..ab134cb9 100644
--- a/ui/latency/latency_info.h
+++ b/ui/latency/latency_info.h
@@ -174,6 +174,9 @@
   // event's scroll_update_delta and the SCROLL_UPDATE_LAST_EVENT_COMPONENT.
   void CoalesceScrollUpdateWith(const LatencyInfo& other);
 
+  // Scale scroll_update_delta and predicted_scroll_update_delta.
+  LatencyInfo ScaledBy(float scale) const;
+
   const LatencyMap& latency_components() const { return latency_components_; }
 
   const SourceEventType& source_event_type() const {
@@ -194,6 +197,12 @@
   const std::string& trace_name() const { return trace_name_; }
   void set_scroll_update_delta(float delta) { scroll_update_delta_ = delta; }
   float scroll_update_delta() const { return scroll_update_delta_; }
+  void set_predicted_scroll_update_delta(float delta) {
+    predicted_scroll_update_delta_ = delta;
+  }
+  float predicted_scroll_update_delta() const {
+    return predicted_scroll_update_delta_;
+  }
 
  private:
   void AddLatencyNumberWithTimestampImpl(LatencyComponentType component,
@@ -226,6 +235,7 @@
   SourceEventType source_event_type_;
 
   float scroll_update_delta_;
+  float predicted_scroll_update_delta_;
 
 #if !defined(OS_IOS)
   friend struct IPC::ParamTraits<ui::LatencyInfo>;
diff --git a/ui/latency/mojo/latency_info.mojom b/ui/latency/mojo/latency_info.mojom
index da021bd..87f8e09 100644
--- a/ui/latency/mojo/latency_info.mojom
+++ b/ui/latency/mojo/latency_info.mojom
@@ -82,4 +82,5 @@
   bool terminated;
   SourceEventType source_event_type;
   float scroll_update_delta;
+  float predicted_scroll_update_delta;
 };
diff --git a/ui/latency/mojo/latency_info_struct_traits.cc b/ui/latency/mojo/latency_info_struct_traits.cc
index 2ca2451..624c8f34 100644
--- a/ui/latency/mojo/latency_info_struct_traits.cc
+++ b/ui/latency/mojo/latency_info_struct_traits.cc
@@ -122,6 +122,12 @@
 }
 
 // static
+float StructTraits<ui::mojom::LatencyInfoDataView, ui::LatencyInfo>::
+    predicted_scroll_update_delta(const ui::LatencyInfo& info) {
+  return info.predicted_scroll_update_delta();
+}
+
+// static
 bool StructTraits<ui::mojom::LatencyInfoDataView, ui::LatencyInfo>::Read(
     ui::mojom::LatencyInfoDataView data,
     ui::LatencyInfo* out) {
@@ -136,6 +142,7 @@
   out->terminated_ = data.terminated();
   out->source_event_type_ = MojoSourceEventTypeToUI(data.source_event_type());
   out->scroll_update_delta_ = data.scroll_update_delta();
+  out->predicted_scroll_update_delta_ = data.predicted_scroll_update_delta();
 
   return true;
 }
diff --git a/ui/latency/mojo/latency_info_struct_traits.h b/ui/latency/mojo/latency_info_struct_traits.h
index 5844fec..e5afdf5 100644
--- a/ui/latency/mojo/latency_info_struct_traits.h
+++ b/ui/latency/mojo/latency_info_struct_traits.h
@@ -55,6 +55,7 @@
   static ui::mojom::SourceEventType source_event_type(
       const ui::LatencyInfo& info);
   static float scroll_update_delta(const ui::LatencyInfo& info);
+  static float predicted_scroll_update_delta(const ui::LatencyInfo& info);
   static bool Read(ui::mojom::LatencyInfoDataView data, ui::LatencyInfo* out);
 };
 
diff --git a/ui/native_theme/native_theme_mac.h b/ui/native_theme/native_theme_mac.h
index 0076b510..3605eae 100644
--- a/ui/native_theme/native_theme_mac.h
+++ b/ui/native_theme/native_theme_mac.h
@@ -70,6 +70,9 @@
                                         bool round_right,
                                         bool focus);
 
+  // Updates cached dark mode status and notifies observers if it has changed.
+  void UpdateDarkModeStatus();
+
  protected:
   friend class NativeTheme;
   friend class base::NoDestructor<NativeThemeMac>;
@@ -88,6 +91,8 @@
       appearance_observer_;
   id high_contrast_notification_token_;
 
+  bool is_dark_mode_ = false;
+
   DISALLOW_COPY_AND_ASSIGN(NativeThemeMac);
 };
 
diff --git a/ui/native_theme/native_theme_mac.mm b/ui/native_theme/native_theme_mac.mm
index de3389f..6246830 100644
--- a/ui/native_theme/native_theme_mac.mm
+++ b/ui/native_theme/native_theme_mac.mm
@@ -19,6 +19,21 @@
 #include "ui/gfx/skia_util.h"
 #include "ui/native_theme/common_theme.h"
 
+namespace {
+
+bool IsDarkMode() {
+  if (@available(macOS 10.14, *)) {
+    NSAppearanceName appearance =
+        [[NSApp effectiveAppearance] bestMatchFromAppearancesWithNames:@[
+          NSAppearanceNameAqua, NSAppearanceNameDarkAqua
+        ]];
+    return [appearance isEqual:NSAppearanceNameDarkAqua];
+  }
+
+  return false;
+}
+}  // namespace
+
 @interface NSWorkspace (Redeclarations)
 
 @property(readonly) BOOL accessibilityDisplayShouldIncreaseContrast;
@@ -29,11 +44,14 @@
 @interface NativeThemeEffectiveAppearanceObserver : NSObject
 @end
 
-@implementation NativeThemeEffectiveAppearanceObserver
+@implementation NativeThemeEffectiveAppearanceObserver {
+  ui::NativeThemeMac* owner_;
+}
 
-- (instancetype)init {
+- (instancetype)initWithOwner:(ui::NativeThemeMac*)owner {
   self = [super init];
   if (self) {
+    owner_ = owner;
     if (@available(macOS 10.14, *)) {
       [NSApp addObserver:self
               forKeyPath:@"effectiveAppearance"
@@ -55,7 +73,7 @@
                       ofObject:(id)object
                         change:(NSDictionary*)change
                        context:(void*)context {
-  ui::NativeTheme::GetInstanceForNativeUi()->NotifyObservers();
+  owner_->UpdateDarkModeStatus();
 }
 
 @end
@@ -262,19 +280,18 @@
 
 bool NativeThemeMac::SystemDarkModeEnabled() const {
   if (@available(macOS 10.14, *)) {
-    NSAppearanceName appearance =
-        [[NSApp effectiveAppearance] bestMatchFromAppearancesWithNames:@[
-          NSAppearanceNameAqua, NSAppearanceNameDarkAqua
-        ]];
-    return [appearance isEqual:NSAppearanceNameDarkAqua];
+    return is_dark_mode_;
+  } else {
+    // Support "--force-dark-mode" in macOS < 10.14.
+    return NativeThemeBase::SystemDarkModeEnabled();
   }
-  return NativeThemeBase::SystemDarkModeEnabled();
 }
 
 NativeThemeMac::NativeThemeMac() {
   if (base::FeatureList::IsEnabled(features::kDarkMode)) {
+    is_dark_mode_ = IsDarkMode();
     appearance_observer_.reset(
-        [[NativeThemeEffectiveAppearanceObserver alloc] init]);
+        [[NativeThemeEffectiveAppearanceObserver alloc] initWithOwner:this]);
   }
   if (@available(macOS 10.10, *)) {
     high_contrast_notification_token_ = [[[NSWorkspace sharedWorkspace]
@@ -302,4 +319,11 @@
   canvas->drawRect(gfx::RectToSkRect(rect), flags);
 }
 
+void NativeThemeMac::UpdateDarkModeStatus() {
+  bool was_dark_mode = is_dark_mode_;
+  is_dark_mode_ = IsDarkMode();
+  if (was_dark_mode != is_dark_mode_)
+    NotifyObservers();
+}
+
 }  // namespace ui
diff --git a/ui/views_bridge_mac/bridged_content_view.mm b/ui/views_bridge_mac/bridged_content_view.mm
index 345b110..2c04b9b 100644
--- a/ui/views_bridge_mac/bridged_content_view.mm
+++ b/ui/views_bridge_mac/bridged_content_view.mm
@@ -568,15 +568,29 @@
 
 // NSView implementation.
 
-// This view must consistently return YES or else dragging a tab may drag the
-// entire window. See r549802 for details.
+// Refuse first responder so that clicking a blank area of the view don't take
+// first responder away from another view. This does not prevent the view
+// becoming first responder via -[NSWindow makeFirstResponder:] when invoked
+// during Init or by FocusManager.
+//
+// The condition is to work around an AppKit quirk. When a window is being
+// ordered front, if its current first responder returns |NO| for this method,
+// it resigns it if it can find another responder in the key loop that replies
+// |YES|.
 - (BOOL)acceptsFirstResponder {
-  return YES;
+  return self.window.firstResponder == self;
+}
+
+// This undocumented method determines which parts of the view prevent
+// server-side window dragging (i.e. aren't draggable without asking the app
+// first). Since Views decides click-by-click whether to handle an event, the
+// whole view is off limits but, since the view's content is rendered out of
+// process and the view is locally transparent, AppKit won't guess that.
+- (NSRect)_opaqueRectForWindowMoveWhenInTitlebar {
+  return self.bounds;
 }
 
 - (BOOL)becomeFirstResponder {
-  if ([[self window] firstResponder] != self)
-    return NO;
   BOOL result = [super becomeFirstResponder];
   if (result && bridge_)
     bridge_->host()->OnIsFirstResponderChanged(true);