diff --git a/BUILD.gn b/BUILD.gn
index 1c4246a..5f7f401 100644
--- a/BUILD.gn
+++ b/BUILD.gn
@@ -297,7 +297,6 @@
       "//net/android:net_junit_tests",
       "//services:service_junit_tests",
       "//testing/android/junit:junit_unit_tests",
-      "//third_party/android_async_task:android_async_task_java",
       "//third_party/catapult/devil",
       "//third_party/errorprone:errorprone_java",
       "//third_party/smhasher:murmurhash3",
diff --git a/DEPS b/DEPS
index e422228..086f5a8 100644
--- a/DEPS
+++ b/DEPS
@@ -105,7 +105,7 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling Skia
   # and whatever else without interference from each other.
-  'skia_revision': '94d57c477fe122c146a034c625cebb6c44ef21b0',
+  'skia_revision': '58a1605d2b9bab077f53b6a223f9e7ce1891d3ea',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling V8
   # and whatever else without interference from each other.
@@ -129,7 +129,7 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling PDFium
   # and whatever else without interference from each other.
-  'pdfium_revision': 'd77e0ed72f73fb63305d04953ef03e2edab82d34',
+  'pdfium_revision': '95b0293a29b235c746db0f01c8462ca89d7a814e',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling openmax_dl
   # and whatever else without interference from each other.
@@ -165,7 +165,7 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling catapult
   # and whatever else without interference from each other.
-  'catapult_revision': '9f46241c6c03d9ffb2422e85a362f0bab5d4215f',
+  'catapult_revision': '10e0a3798e47192037de47c9f258e16aa18cccbf',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling libFuzzer
   # and whatever else without interference from each other.
@@ -540,7 +540,7 @@
 
   # Build tools for Chrome OS. Note: This depends on third_party/pyelftools.
   'src/third_party/chromite': {
-      'url': Var('chromium_git') + '/chromiumos/chromite.git' + '@' + '45a546a3088515407c6e59c0681cbab3541cfec5',
+      'url': Var('chromium_git') + '/chromiumos/chromite.git' + '@' + '670e331c4d8724a1b630b3238c58758da18770b0',
       'condition': 'checkout_linux',
   },
 
@@ -809,7 +809,7 @@
   },
 
   'src/third_party/libvpx/source/libvpx':
-    Var('chromium_git') + '/webm/libvpx.git' + '@' +  '583859d7395ca70c3b1ca0acc1258a720470a807',
+    Var('chromium_git') + '/webm/libvpx.git' + '@' +  '03abd2c8f358549a445dce452e26aa72d3853e55',
 
   'src/third_party/libwebm/source':
     Var('chromium_git') + '/webm/libwebm.git' + '@' + '01c1d1d76f139345c442bfc8e61b4e1cba809059',
@@ -1035,7 +1035,7 @@
     Var('chromium_git') + '/external/khronosgroup/webgl.git' + '@' + 'a5c263cc63ffc2cc189b5214074c8792067c1853',
 
   'src/third_party/webrtc':
-    Var('webrtc_git') + '/src.git' + '@' + '3bc977a420229a9dd45dd64159aba45e210fe3dc',
+    Var('webrtc_git') + '/src.git' + '@' + 'd000b0a32ee7924d151ea1fd89a82c5855dc094e',
 
   'src/third_party/xdg-utils': {
       'url': Var('chromium_git') + '/chromium/deps/xdg-utils.git' + '@' + 'd80274d5869b17b8c9067a1022e4416ee7ed5e0d',
diff --git a/android_webview/BUILD.gn b/android_webview/BUILD.gn
index cf06c60..5a4ec1e 100644
--- a/android_webview/BUILD.gn
+++ b/android_webview/BUILD.gn
@@ -524,8 +524,6 @@
     "browser/aw_resource_context.h",
     "browser/aw_safe_browsing_blocking_page.cc",
     "browser/aw_safe_browsing_blocking_page.h",
-    "browser/aw_safe_browsing_config_helper.cc",
-    "browser/aw_safe_browsing_config_helper.h",
     "browser/aw_safe_browsing_resource_throttle.cc",
     "browser/aw_safe_browsing_resource_throttle.h",
     "browser/aw_safe_browsing_ui_manager.cc",
diff --git a/android_webview/browser/aw_contents_statics.cc b/android_webview/browser/aw_contents_statics.cc
index 081c02f5..76faeb22 100644
--- a/android_webview/browser/aw_contents_statics.cc
+++ b/android_webview/browser/aw_contents_statics.cc
@@ -5,7 +5,6 @@
 #include "android_webview/browser/aw_browser_context.h"
 #include "android_webview/browser/aw_contents.h"
 #include "android_webview/browser/aw_contents_io_thread_client.h"
-#include "android_webview/browser/aw_safe_browsing_config_helper.h"
 #include "android_webview/browser/aw_safe_browsing_whitelist_manager.h"
 #include "android_webview/browser/net/aw_url_request_context_getter.h"
 #include "base/android/jni_array.h"
@@ -98,21 +97,6 @@
 }
 
 // static
-jboolean JNI_AwContentsStatics_GetSafeBrowsingEnabledByManifest(
-    JNIEnv* env,
-    const JavaParamRef<jclass>&) {
-  return AwSafeBrowsingConfigHelper::GetSafeBrowsingEnabledByManifest();
-}
-
-// static
-void JNI_AwContentsStatics_SetSafeBrowsingEnabledByManifest(
-    JNIEnv* env,
-    const JavaParamRef<jclass>&,
-    jboolean enable) {
-  AwSafeBrowsingConfigHelper::SetSafeBrowsingEnabledByManifest(enable);
-}
-
-// static
 void JNI_AwContentsStatics_SetSafeBrowsingWhitelist(
     JNIEnv* env,
     const JavaParamRef<jclass>&,
diff --git a/android_webview/browser/aw_safe_browsing_config_helper.cc b/android_webview/browser/aw_safe_browsing_config_helper.cc
deleted file mode 100644
index 8908f7b..0000000
--- a/android_webview/browser/aw_safe_browsing_config_helper.cc
+++ /dev/null
@@ -1,34 +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 "android_webview/browser/aw_safe_browsing_config_helper.h"
-
-#include "base/android/scoped_java_ref.h"
-#include "base/lazy_instance.h"
-#include "base/synchronization/lock.h"
-
-namespace {
-// g_safebrowsing_enabled_by_manifest can be set and read from different
-// threads.
-base::LazyInstance<base::Lock>::Leaky g_safebrowsing_enabled_by_manifest_lock =
-    LAZY_INSTANCE_INITIALIZER;
-bool g_safebrowsing_enabled_by_manifest = false;
-}  // namespace
-
-namespace android_webview {
-
-// static
-void AwSafeBrowsingConfigHelper::SetSafeBrowsingEnabledByManifest(
-    bool enabled) {
-  base::AutoLock lock(g_safebrowsing_enabled_by_manifest_lock.Get());
-  g_safebrowsing_enabled_by_manifest = enabled;
-}
-
-// static
-bool AwSafeBrowsingConfigHelper::GetSafeBrowsingEnabledByManifest() {
-  base::AutoLock lock(g_safebrowsing_enabled_by_manifest_lock.Get());
-  return g_safebrowsing_enabled_by_manifest;
-}
-
-}  // namespace android_webview
diff --git a/android_webview/browser/aw_safe_browsing_config_helper.h b/android_webview/browser/aw_safe_browsing_config_helper.h
deleted file mode 100644
index a448c0e..0000000
--- a/android_webview/browser/aw_safe_browsing_config_helper.h
+++ /dev/null
@@ -1,24 +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 ANDROID_WEBVIEW_BROWSER_AW_SAFE_BROWSING_CONFIG_HELPER_H_
-#define ANDROID_WEBVIEW_BROWSER_AW_SAFE_BROWSING_CONFIG_HELPER_H_
-
-#include "base/macros.h"
-
-namespace android_webview {
-
-class AwSafeBrowsingConfigHelper {
- public:
-  static bool GetSafeBrowsingEnabledByManifest();
-  static void SetSafeBrowsingEnabledByManifest(bool enabled);
-
- private:
-  AwSafeBrowsingConfigHelper();
-  DISALLOW_COPY_AND_ASSIGN(AwSafeBrowsingConfigHelper);
-};
-
-}  // namespace android_webview
-
-#endif  // ANDROID_WEBVIEW_BROWSER_AW_SAFE_BROWSING_CONFIG_HELPER_H_
diff --git a/android_webview/browser/renderer_host/aw_resource_dispatcher_host_delegate.cc b/android_webview/browser/renderer_host/aw_resource_dispatcher_host_delegate.cc
index d828824..bbe8c8ca 100644
--- a/android_webview/browser/renderer_host/aw_resource_dispatcher_host_delegate.cc
+++ b/android_webview/browser/renderer_host/aw_resource_dispatcher_host_delegate.cc
@@ -11,7 +11,6 @@
 #include "android_webview/browser/aw_contents_client_bridge.h"
 #include "android_webview/browser/aw_contents_io_thread_client.h"
 #include "android_webview/browser/aw_resource_context.h"
-#include "android_webview/browser/aw_safe_browsing_config_helper.h"
 #include "android_webview/browser/aw_safe_browsing_resource_throttle.h"
 #include "android_webview/browser/net/aw_web_resource_request.h"
 #include "android_webview/browser/renderer_host/auto_login_parser.h"
diff --git a/android_webview/glue/java/src/com/android/webview/chromium/WebViewChromium.java b/android_webview/glue/java/src/com/android/webview/chromium/WebViewChromium.java
index c6fff28..aeec235 100644
--- a/android_webview/glue/java/src/com/android/webview/chromium/WebViewChromium.java
+++ b/android_webview/glue/java/src/com/android/webview/chromium/WebViewChromium.java
@@ -175,10 +175,7 @@
             //   comes from a single thread. (Note in JB MR2 this exception was in WebView.java).
             if (mAppTargetSdkVersion >= Build.VERSION_CODES.JELLY_BEAN_MR2) {
                 mFactory.startYourEngines(false);
-                try (ScopedSysTraceEvent e2 = ScopedSysTraceEvent.scoped(
-                             "WebViewChromium.checkThreadInsideInit")) {
-                    checkThread();
-                }
+                checkThread();
             } else if (!mFactory.hasStarted()) {
                 if (Looper.myLooper() == Looper.getMainLooper()) {
                     mFactory.startYourEngines(true);
@@ -198,11 +195,8 @@
             final boolean doNotUpdateSelectionOnMutatingSelectionRange =
                     mAppTargetSdkVersion <= Build.VERSION_CODES.M;
 
-            try (ScopedSysTraceEvent e2 = ScopedSysTraceEvent.scoped(
-                         "WebViewChromiumFactoryProvider.createWebViewContentsClientAdapter")) {
-                mContentsClientAdapter =
-                        mFactory.createWebViewContentsClientAdapter(mWebView, mContext);
-            }
+            mContentsClientAdapter =
+                    mFactory.createWebViewContentsClientAdapter(mWebView, mContext);
             try (ScopedSysTraceEvent e2 =
                             ScopedSysTraceEvent.scoped("WebViewChromium.ContentSettingsAdapter")) {
                 mWebSettings = new ContentSettingsAdapter(new AwSettings(mContext,
diff --git a/android_webview/glue/java/src/com/android/webview/chromium/WebViewContentsClientAdapter.java b/android_webview/glue/java/src/com/android/webview/chromium/WebViewContentsClientAdapter.java
index a7fbc79..57c3099 100644
--- a/android_webview/glue/java/src/com/android/webview/chromium/WebViewContentsClientAdapter.java
+++ b/android_webview/glue/java/src/com/android/webview/chromium/WebViewContentsClientAdapter.java
@@ -127,20 +127,17 @@
     @SuppressLint("HandlerLeak")
     WebViewContentsClientAdapter(WebView webView, Context context,
             WebViewDelegate webViewDelegate) {
-        try (ScopedSysTraceEvent e = ScopedSysTraceEvent.scoped(
-                     "WebViewContentsClientAdapter.beginningOfConstructor")) {
-            if (webView == null || webViewDelegate == null) {
-                throw new IllegalArgumentException("webView or delegate can't be null.");
-            }
-
-            if (context == null) {
-                throw new IllegalArgumentException("context can't be null.");
-            }
-
-            mContext = context;
-            mWebView = webView;
-            mWebViewDelegate = webViewDelegate;
+        if (webView == null || webViewDelegate == null) {
+            throw new IllegalArgumentException("webView or delegate can't be null.");
         }
+
+        if (context == null) {
+            throw new IllegalArgumentException("context can't be null.");
+        }
+
+        mContext = context;
+        mWebView = webView;
+        mWebViewDelegate = webViewDelegate;
         try (ScopedSysTraceEvent event =
                         ScopedSysTraceEvent.scoped("WebViewContentsClientAdapter.constructor")) {
             mSupportLibClient = new SupportLibWebViewContentsClientAdapter();
diff --git a/android_webview/java/src/org/chromium/android_webview/AwContentsClientCallbackHelper.java b/android_webview/java/src/org/chromium/android_webview/AwContentsClientCallbackHelper.java
index 3cdb659..3fa731a 100644
--- a/android_webview/java/src/org/chromium/android_webview/AwContentsClientCallbackHelper.java
+++ b/android_webview/java/src/org/chromium/android_webview/AwContentsClientCallbackHelper.java
@@ -255,11 +255,8 @@
     }
 
     public AwContentsClientCallbackHelper(Looper looper, AwContentsClient contentsClient) {
-        try (ScopedSysTraceEvent e =
-                        ScopedSysTraceEvent.scoped("AwContentsClientCallbackHelper.constructor")) {
-            mHandler = new MyHandler(looper);
-            mContentsClient = contentsClient;
-        }
+        mHandler = new MyHandler(looper);
+        mContentsClient = contentsClient;
     }
 
     // Public for tests.
diff --git a/android_webview/java/src/org/chromium/android_webview/AwContentsStatics.java b/android_webview/java/src/org/chromium/android_webview/AwContentsStatics.java
index fd88519..2215bf06 100644
--- a/android_webview/java/src/org/chromium/android_webview/AwContentsStatics.java
+++ b/android_webview/java/src/org/chromium/android_webview/AwContentsStatics.java
@@ -84,15 +84,6 @@
         nativeSetServiceWorkerIoThreadClient(ioThreadClient, browserContext);
     }
 
-    // Can be called from any thread.
-    public static boolean getSafeBrowsingEnabledByManifest() {
-        return nativeGetSafeBrowsingEnabledByManifest();
-    }
-
-    public static void setSafeBrowsingEnabledByManifest(boolean enable) {
-        nativeSetSafeBrowsingEnabledByManifest(enable);
-    }
-
     @CalledByNative
     private static void safeBrowsingWhitelistAssigned(Callback<Boolean> callback, boolean success) {
         if (callback == null) return;
@@ -152,8 +143,6 @@
     private static native String nativeGetProductVersion();
     private static native void nativeSetServiceWorkerIoThreadClient(
             AwContentsIoThreadClient ioThreadClient, AwBrowserContext browserContext);
-    private static native boolean nativeGetSafeBrowsingEnabledByManifest();
-    private static native void nativeSetSafeBrowsingEnabledByManifest(boolean enable);
     private static native void nativeSetSafeBrowsingWhitelist(
             String[] urls, Callback<Boolean> callback);
     private static native void nativeSetCheckClearTextPermitted(boolean permitted);
diff --git a/android_webview/java/src/org/chromium/android_webview/AwSafeBrowsingConfigHelper.java b/android_webview/java/src/org/chromium/android_webview/AwSafeBrowsingConfigHelper.java
index 9eeff36..26169229 100644
--- a/android_webview/java/src/org/chromium/android_webview/AwSafeBrowsingConfigHelper.java
+++ b/android_webview/java/src/org/chromium/android_webview/AwSafeBrowsingConfigHelper.java
@@ -27,6 +27,7 @@
     private static final boolean DEFAULT_USER_OPT_IN = false;
 
     private static volatile Boolean sSafeBrowsingUserOptIn;
+    private static volatile boolean sEnabledByManifest;
 
     // Used to record the UMA histogram SafeBrowsing.WebView.AppOptIn. Since these values are
     // persisted to logs, they should never be renumbered nor reused.
@@ -60,6 +61,14 @@
                 "SafeBrowsing.WebView.UserOptIn", value, UserOptIn.COUNT);
     }
 
+    public static void setSafeBrowsingEnabledByManifest(boolean enabled) {
+        sEnabledByManifest = enabled;
+    }
+
+    public static boolean getSafeBrowsingEnabledByManifest() {
+        return sEnabledByManifest;
+    }
+
     // Should only be called once during startup. Calling this multiple times will skew UMA metrics.
     public static void maybeEnableSafeBrowsingFromManifest(final Context appContext) {
         try (ScopedSysTraceEvent e = ScopedSysTraceEvent.scoped(
@@ -75,7 +84,7 @@
 
             // If the app specifies something, fallback to the app's preference, otherwise check for
             // the existence of the CLI switch.
-            AwContentsStatics.setSafeBrowsingEnabledByManifest(
+            setSafeBrowsingEnabledByManifest(
                     appOptIn == null ? !isDisabledByCommandLine() : appOptIn);
 
             Callback<Boolean> cb = verifyAppsValue -> {
diff --git a/android_webview/java/src/org/chromium/android_webview/AwServiceWorkerController.java b/android_webview/java/src/org/chromium/android_webview/AwServiceWorkerController.java
index c3c52552..571210c 100644
--- a/android_webview/java/src/org/chromium/android_webview/AwServiceWorkerController.java
+++ b/android_webview/java/src/org/chromium/android_webview/AwServiceWorkerController.java
@@ -84,7 +84,7 @@
 
         @Override
         public boolean getSafeBrowsingEnabled() {
-            return AwContentsStatics.getSafeBrowsingEnabledByManifest();
+            return AwSafeBrowsingConfigHelper.getSafeBrowsingEnabledByManifest();
         }
     }
 
diff --git a/android_webview/java/src/org/chromium/android_webview/AwSettings.java b/android_webview/java/src/org/chromium/android_webview/AwSettings.java
index 23f743c..90d012e1 100644
--- a/android_webview/java/src/org/chromium/android_webview/AwSettings.java
+++ b/android_webview/java/src/org/chromium/android_webview/AwSettings.java
@@ -110,7 +110,7 @@
     // Although this bit is stored on AwSettings it is actually controlled via the CookieManager.
     private boolean mAcceptThirdPartyCookies;
 
-    // if null, default to AwContentsStatics.getSafeBrowsingEnabledByManifest()
+    // if null, default to AwSafeBrowsingConfigHelper.getSafeBrowsingEnabledByManifest()
     private Boolean mSafeBrowsingEnabled;
 
     private final boolean mSupportLegacyQuirks;
@@ -378,7 +378,7 @@
             if (userOptIn != null && !userOptIn) return false;
 
             if (mSafeBrowsingEnabled == null) {
-                return AwContentsStatics.getSafeBrowsingEnabledByManifest();
+                return AwSafeBrowsingConfigHelper.getSafeBrowsingEnabledByManifest();
             }
             return mSafeBrowsingEnabled;
         }
diff --git a/android_webview/lib/aw_main_delegate.cc b/android_webview/lib/aw_main_delegate.cc
index 4a25fd9c..70089e3383 100644
--- a/android_webview/lib/aw_main_delegate.cc
+++ b/android_webview/lib/aw_main_delegate.cc
@@ -8,7 +8,6 @@
 
 #include "android_webview/browser/aw_content_browser_client.h"
 #include "android_webview/browser/aw_media_url_interceptor.h"
-#include "android_webview/browser/aw_safe_browsing_config_helper.h"
 #include "android_webview/browser/browser_view_renderer.h"
 #include "android_webview/browser/command_line_helper.h"
 #include "android_webview/browser/deferred_gpu_command_service.h"
diff --git a/ash/login/login_screen_controller.cc b/ash/login/login_screen_controller.cc
index 3d8f5c8e..a7c4ebf9 100644
--- a/ash/login/login_screen_controller.cc
+++ b/ash/login/login_screen_controller.cc
@@ -417,6 +417,10 @@
   login_screen_client_->LaunchKioskApp(app_id);
 }
 
+void LoginScreenController::LaunchArcKioskApp(const AccountId& account_id) {
+  login_screen_client_->LaunchArcKioskApp(account_id);
+}
+
 void LoginScreenController::DoAuthenticateUser(const AccountId& account_id,
                                                const std::string& password,
                                                bool authenticated_by_pin,
diff --git a/ash/login/login_screen_controller.h b/ash/login/login_screen_controller.h
index 070d1ee..df23ead 100644
--- a/ash/login/login_screen_controller.h
+++ b/ash/login/login_screen_controller.h
@@ -83,6 +83,7 @@
                                            const std::string& locale);
   void ShowFeedback();
   void LaunchKioskApp(const std::string& app_id);
+  void LaunchArcKioskApp(const AccountId& account_id);
 
   // Add or remove an observer.
   void AddObserver(LoginScreenControllerObserver* observer);
diff --git a/ash/login/mock_login_screen_client.h b/ash/login/mock_login_screen_client.h
index e9632fa..b765c3cb2 100644
--- a/ash/login/mock_login_screen_client.h
+++ b/ash/login/mock_login_screen_client.h
@@ -69,6 +69,7 @@
                void(const AccountId& account_id, const std::string& locale));
   MOCK_METHOD0(ShowFeedback, void());
   MOCK_METHOD1(LaunchKioskApp, void(const std::string& app_id));
+  MOCK_METHOD1(LaunchArcKioskApp, void(const AccountId& account_id));
 
  private:
   bool authenticate_user_callback_result_ = true;
diff --git a/ash/login/ui/lock_debug_view.cc b/ash/login/ui/lock_debug_view.cc
index 2a10ee9..24904e7 100644
--- a/ash/login/ui/lock_debug_view.cc
+++ b/ash/login/ui/lock_debug_view.cc
@@ -268,7 +268,8 @@
 
   void AddKioskApp(ShelfWidget* shelf_widget) {
     mojom::KioskAppInfoPtr app_info = mojom::KioskAppInfo::New();
-    app_info->app_id = kDebugKioskAppId;
+    app_info->identifier = mojom::KioskAppIdentifier::New();
+    app_info->identifier->set_app_id(kDebugKioskAppId);
     app_info->name = base::UTF8ToUTF16(kDebugKioskAppName);
     kiosk_apps_.push_back(std::move(app_info));
     shelf_widget->SetLoginKioskApps(mojo::Clone(kiosk_apps_));
diff --git a/ash/public/interfaces/kiosk_app_info.mojom b/ash/public/interfaces/kiosk_app_info.mojom
index db0accb..4374043f9 100644
--- a/ash/public/interfaces/kiosk_app_info.mojom
+++ b/ash/public/interfaces/kiosk_app_info.mojom
@@ -4,15 +4,21 @@
 
 module ash.mojom;
 
+import "components/account_id/interfaces/account_id.mojom";
 import "mojo/public/mojom/base/string16.mojom";
 import "ui/gfx/image/mojo/image.mojom";
 
+union KioskAppIdentifier {
+  // For chrome kiosk apps only, the extension app id.
+  string app_id;
+  // For ARC kiosk apps only, the account id for the app.
+  signin.mojom.AccountId account_id;
+};
+
 // Metadata about a kiosk app. Used for display in the kiosk app menu in the
 // login screen shelf.
 struct KioskAppInfo {
-  // For chrome kiosk apps, the extension app id. For ARC apps, an id generated
-  // from the package name + class name
-  string app_id;
+  KioskAppIdentifier identifier;
   mojo_base.mojom.String16 name;
   gfx.mojom.ImageSkia icon;
 };
\ No newline at end of file
diff --git a/ash/public/interfaces/login_screen.mojom b/ash/public/interfaces/login_screen.mojom
index 3d52c2b..a3eb969 100644
--- a/ash/public/interfaces/login_screen.mojom
+++ b/ash/public/interfaces/login_screen.mojom
@@ -228,4 +228,7 @@
 
   // Launch the specific kiosk app.
   LaunchKioskApp(string app_id);
+
+  // Launch the specific ARC++ kiosk app.
+  LaunchArcKioskApp(signin.mojom.AccountId account_id);
 };
diff --git a/ash/shelf/login_shelf_view.cc b/ash/shelf/login_shelf_view.cc
index c389987..4959b84 100644
--- a/ash/shelf/login_shelf_view.cc
+++ b/ash/shelf/login_shelf_view.cc
@@ -213,8 +213,20 @@
     // the state is reset (when login screen reappears).
     is_launch_enabled_ = false;
 
-    Shell::Get()->login_screen_controller()->LaunchKioskApp(
-        kiosk_apps_[command_id]->app_id);
+    const mojom::KioskAppInfoPtr& kiosk_app = kiosk_apps_[command_id];
+
+    switch (kiosk_app->identifier->which()) {
+      case mojom::KioskAppIdentifier::Tag::ACCOUNT_ID:
+        Shell::Get()->login_screen_controller()->LaunchArcKioskApp(
+            kiosk_app->identifier->get_account_id());
+        return;
+      case mojom::KioskAppIdentifier::Tag::APP_ID:
+        Shell::Get()->login_screen_controller()->LaunchKioskApp(
+            kiosk_app->identifier->get_app_id());
+        return;
+      default:
+        NOTREACHED();
+    }
   }
 
   bool IsCommandIdChecked(int command_id) const override { return false; }
diff --git a/base/BUILD.gn b/base/BUILD.gn
index cc921b3..d496283 100644
--- a/base/BUILD.gn
+++ b/base/BUILD.gn
@@ -2877,6 +2877,7 @@
       "android/java/src/org/chromium/base/memory/MemoryPressureMonitor.java",
       "android/java/src/org/chromium/base/memory/MemoryPressureCallback.java",
       "android/java/src/org/chromium/base/memory/MemoryPressureUma.java",
+      "//third_party/android_async_task/java/src/org/chromium/base/AsyncTask.java",
     ]
 
     # New versions of BuildConfig.java and NativeLibraries.java
@@ -3011,7 +3012,11 @@
     java_files = [
       "android/junit/src/org/chromium/base/metrics/test/ShadowRecordHistogram.java",
       "test/android/junit/src/org/chromium/base/test/BaseRobolectricTestRunner.java",
+      "test/android/junit/src/org/chromium/base/test/asynctask/BackgroundShadowAsyncTask.java",
+      "test/android/junit/src/org/chromium/base/test/asynctask/CustomShadowAsyncTask.java",
       "test/android/junit/src/org/chromium/base/test/util/TestRunnerTestRule.java",
+      "//third_party/robolectric/custom_asynctask/java/src/org/chromium/base/test/asynctask/ShadowAsyncTask.java",
+      "//third_party/robolectric/custom_asynctask/java/src/org/chromium/base/test/asynctask/ShadowAsyncTaskBridge.java",
     ]
     deps = [
       ":base_java",
diff --git a/base/profiler/stack_sampling_profiler.h b/base/profiler/stack_sampling_profiler.h
index df34df4..864f0e9 100644
--- a/base/profiler/stack_sampling_profiler.h
+++ b/base/profiler/stack_sampling_profiler.h
@@ -14,7 +14,6 @@
 
 #include "base/atomicops.h"
 #include "base/base_export.h"
-#include "base/callback.h"
 #include "base/files/file_path.h"
 #include "base/macros.h"
 #include "base/strings/string16.h"
@@ -270,18 +269,6 @@
     DISALLOW_COPY_AND_ASSIGN(ProfileBuilder);
   };
 
-  // The callback type used to collect a completed profile. The passed |profile|
-  // is move-only. Other threads, including the UI thread, may block on callback
-  // completion so this should run as quickly as possible.
-  //
-  // IMPORTANT NOTE: The callback is invoked on a thread the profiler
-  // constructs, rather than on the thread used to construct the profiler, and
-  // thus the callback must be callable on any thread. For threads with message
-  // loops that create StackSamplingProfilers, posting a task to the message
-  // loop with the moved (i.e. std::move) profile is the thread-safe callback
-  // implementation.
-  using CompletedCallback = Callback<void(CallStackProfile)>;
-
   // Creates a profiler for the CURRENT thread. An optional |test_delegate| can
   // be supplied by tests. The caller must ensure that this object gets
   // destroyed before the current thread exits.
diff --git a/base/test/android/junit/src/org/chromium/base/test/asynctask/BackgroundShadowAsyncTask.java b/base/test/android/junit/src/org/chromium/base/test/asynctask/BackgroundShadowAsyncTask.java
new file mode 100644
index 0000000..c75e7948
--- /dev/null
+++ b/base/test/android/junit/src/org/chromium/base/test/asynctask/BackgroundShadowAsyncTask.java
@@ -0,0 +1,72 @@
+// 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.
+
+package org.chromium.base.test.asynctask;
+
+import static org.junit.Assert.fail;
+
+import org.robolectric.annotation.Implementation;
+import org.robolectric.annotation.Implements;
+import org.robolectric.shadows.ShadowApplication;
+
+import org.chromium.base.AsyncTask;
+
+import java.util.concurrent.Callable;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+
+/**
+ * Executes async tasks on a background thread and waits on the result on the main thread.
+ * This is useful for users of AsyncTask on Roboelectric who check if the code is actually being
+ * run on a background thread (i.e. through the use of {@link ThreadUtils#runningOnUiThread()}).
+ * @param <Params>     type for execute function parameters
+ * @param <Progress>   type for reporting Progress
+ * @param <Result>     type for reporting result
+ */
+@Implements(AsyncTask.class)
+public class BackgroundShadowAsyncTask<Params, Progress, Result>
+        extends ShadowAsyncTask<Params, Progress, Result> {
+    private static final ExecutorService sExecutorService = Executors.newSingleThreadExecutor();
+
+    @Override
+    @Implementation
+    @SafeVarargs
+    public final AsyncTask<Params, Progress, Result> execute(final Params... params) {
+        try {
+            return sExecutorService
+                    .submit(new Callable<AsyncTask<Params, Progress, Result>>() {
+                        @Override
+                        public AsyncTask<Params, Progress, Result> call() throws Exception {
+                            return BackgroundShadowAsyncTask.super.execute(params);
+                        }
+                    })
+                    .get();
+        } catch (Exception ex) {
+            fail(ex.getMessage());
+            return null;
+        }
+    }
+
+    @Override
+    @Implementation
+    public final Result get() {
+        try {
+            runBackgroundTasks();
+            return BackgroundShadowAsyncTask.super.get();
+        } catch (Exception e) {
+            return null;
+        }
+    }
+
+    public static void runBackgroundTasks() throws Exception {
+        sExecutorService
+                .submit(new Runnable() {
+                    @Override
+                    public void run() {
+                        ShadowApplication.runBackgroundTasks();
+                    }
+                })
+                .get();
+    }
+}
diff --git a/base/test/android/junit/src/org/chromium/base/test/asynctask/CustomShadowAsyncTask.java b/base/test/android/junit/src/org/chromium/base/test/asynctask/CustomShadowAsyncTask.java
new file mode 100644
index 0000000..bd581c1
--- /dev/null
+++ b/base/test/android/junit/src/org/chromium/base/test/asynctask/CustomShadowAsyncTask.java
@@ -0,0 +1,32 @@
+// 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.
+
+package org.chromium.base.test.asynctask;
+
+import org.robolectric.annotation.Implementation;
+import org.robolectric.annotation.Implements;
+
+import org.chromium.base.AsyncTask;
+
+import java.util.concurrent.Executor;
+
+/**
+ * Forces async tasks to execute with the default executor.
+ * This works around Robolectric not working out of the box with custom executors.
+ *
+ * @param <Params>
+ * @param <Progress>
+ * @param <Result>
+ */
+@Implements(AsyncTask.class)
+public class CustomShadowAsyncTask<Params, Progress, Result>
+        extends ShadowAsyncTask<Params, Progress, Result> {
+    @SafeVarargs
+    @Override
+    @Implementation
+    public final AsyncTask<Params, Progress, Result> executeOnExecutor(
+            Executor executor, Params... params) {
+        return super.execute(params);
+    }
+}
diff --git a/base/test/launcher/test_launcher.cc b/base/test/launcher/test_launcher.cc
index a686b21..36cf967 100644
--- a/base/test/launcher/test_launcher.cc
+++ b/base/test/launcher/test_launcher.cc
@@ -321,8 +321,7 @@
   DCHECK(!new_options.job_handle);
 
   zx::job job_handle;
-  zx_status_t result =
-      zx::job::create(*zx::unowned<zx::job>(GetDefaultJob()), 0, &job_handle);
+  zx_status_t result = zx::job::create(GetDefaultJob(), 0, &job_handle);
   ZX_CHECK(ZX_OK == result, result) << "zx_job_create";
   new_options.job_handle = job_handle.get();
 #endif  // defined(OS_FUCHSIA)
diff --git a/base/test/test_mock_time_task_runner.cc b/base/test/test_mock_time_task_runner.cc
index c74d14e0..a6688da2 100644
--- a/base/test/test_mock_time_task_runner.cc
+++ b/base/test/test_mock_time_task_runner.cc
@@ -50,36 +50,59 @@
   DISALLOW_COPY_AND_ASSIGN(LegacyMockClock);
 };
 
-// A SingleThreadTaskRunner which forwards everything to its |target_|. This is
-// useful to break ownership chains when it is known that |target_| will outlive
-// the NonOwningProxyTaskRunner it's injected into. In particular,
-// TestMockTimeTaskRunner is forced to be ref-counted by virtue of being a
-// SingleThreadTaskRunner. As such it is impossible for it to have a
-// ThreadTaskRunnerHandle member that points back to itself as the
-// ThreadTaskRunnerHandle which it owns would hold a ref back to it. To break
-// this dependency cycle, the ThreadTaskRunnerHandle is instead handed a
-// NonOwningProxyTaskRunner which allows the TestMockTimeTaskRunner to not hand
-// a ref to its ThreadTaskRunnerHandle while promising in return that it will
-// outlive that ThreadTaskRunnerHandle instance.
-class NonOwningProxyTaskRunner : public SingleThreadTaskRunner {
+}  // namespace
+
+// A SingleThreadTaskRunner which forwards everything to its |target_|. This
+// serves two purposes:
+// 1) If a ThreadTaskRunnerHandle owned by TestMockTimeTaskRunner were to be
+//    set to point to that TestMockTimeTaskRunner, a reference cycle would
+//    result.  As |target_| here is a non-refcounting raw pointer, the cycle is
+//    broken.
+// 2) Since SingleThreadTaskRunner is ref-counted, it's quite easy for it to
+//    accidentally get captured between tests in a singleton somewhere.
+//    Indirecting via NonOwningProxyTaskRunner permits TestMockTimeTaskRunner
+//    to be cleaned up (removing the RunLoop::Delegate in the kBoundToThread
+//    mode), and to also cleanly flag any actual attempts to use the leaked
+//    task runner.
+class TestMockTimeTaskRunner::NonOwningProxyTaskRunner
+    : public SingleThreadTaskRunner {
  public:
   explicit NonOwningProxyTaskRunner(SingleThreadTaskRunner* target)
       : target_(target) {
     DCHECK(target_);
   }
 
+  // Detaches this NonOwningProxyTaskRunner instance from its |target_|. It is
+  // invalid to post tasks after this point but RunsTasksInCurrentSequence()
+  // will still pass on the original thread for convenience with legacy code.
+  void Detach() {
+    AutoLock scoped_lock(lock_);
+    target_ = nullptr;
+  }
+
   // SingleThreadTaskRunner:
   bool RunsTasksInCurrentSequence() const override {
-    return target_->RunsTasksInCurrentSequence();
+    AutoLock scoped_lock(lock_);
+    if (target_)
+      return target_->RunsTasksInCurrentSequence();
+    return thread_checker_.CalledOnValidThread();
   }
   bool PostDelayedTask(const Location& from_here,
                        OnceClosure task,
                        TimeDelta delay) override {
+    AutoLock scoped_lock(lock_);
+    CHECK(target_)
+        << "Attempt to post a task on a deleted TestMockTimeTaskRunner";
+
     return target_->PostDelayedTask(from_here, std::move(task), delay);
   }
   bool PostNonNestableDelayedTask(const Location& from_here,
                                   OnceClosure task,
                                   TimeDelta delay) override {
+    AutoLock scoped_lock(lock_);
+    CHECK(target_)
+        << "Attempt to post a task on a deleted TestMockTimeTaskRunner";
+
     return target_->PostNonNestableDelayedTask(from_here, std::move(task),
                                                delay);
   }
@@ -88,13 +111,15 @@
   friend class RefCountedThreadSafe<NonOwningProxyTaskRunner>;
   ~NonOwningProxyTaskRunner() override = default;
 
-  SingleThreadTaskRunner* const target_;
+  mutable Lock lock_;
+  SingleThreadTaskRunner* target_;  // guarded by lock_
+
+  // Used to implement RunsTasksInCurrentSequence, without relying on |target_|.
+  ThreadCheckerImpl thread_checker_;
 
   DISALLOW_COPY_AND_ASSIGN(NonOwningProxyTaskRunner);
 };
 
-}  // namespace
-
 // TestMockTimeTaskRunner::TestOrderedPendingTask -----------------------------
 
 // Subclass of TestPendingTask which has a strictly monotonically increasing ID
@@ -177,15 +202,18 @@
     : now_(start_time),
       now_ticks_(start_ticks),
       tasks_lock_cv_(&tasks_lock_),
+      proxy_task_runner_(MakeRefCounted<NonOwningProxyTaskRunner>(this)),
       mock_clock_(this) {
   if (type == Type::kBoundToThread) {
     RunLoop::RegisterDelegateForCurrentThread(this);
-    thread_task_runner_handle_ = std::make_unique<ThreadTaskRunnerHandle>(
-        MakeRefCounted<NonOwningProxyTaskRunner>(this));
+    thread_task_runner_handle_ =
+        std::make_unique<ThreadTaskRunnerHandle>(proxy_task_runner_);
   }
 }
 
-TestMockTimeTaskRunner::~TestMockTimeTaskRunner() = default;
+TestMockTimeTaskRunner::~TestMockTimeTaskRunner() {
+  proxy_task_runner_->Detach();
+}
 
 void TestMockTimeTaskRunner::FastForwardBy(TimeDelta delta) {
   DCHECK(thread_checker_.CalledOnValidThread());
@@ -211,10 +239,25 @@
 }
 
 void TestMockTimeTaskRunner::ClearPendingTasks() {
-  DCHECK(thread_checker_.CalledOnValidThread());
   AutoLock scoped_lock(tasks_lock_);
-  while (!tasks_.empty())
-    tasks_.pop();
+  // This is repeated in case task destruction triggers further tasks.
+  while (!tasks_.empty()) {
+    TaskPriorityQueue cleanup_tasks;
+    tasks_.swap(cleanup_tasks);
+
+    // Destroy task objects with |tasks_lock_| released. Task deletion can cause
+    // calls to NonOwningProxyTaskRunner::RunsTasksInCurrentSequence()
+    // (e.g. for DCHECKs), which causes |NonOwningProxyTaskRunner::lock_| to be
+    // grabbed.
+    //
+    // On the other hand, calls from NonOwningProxyTaskRunner::PostTask ->
+    // TestMockTimeTaskRunner::PostTask acquire locks as
+    // |NonOwningProxyTaskRunner::lock_| followed by |tasks_lock_|, so it's
+    // desirable to avoid the reverse order, for deadlock freedom.
+    AutoUnlock scoped_unlock(tasks_lock_);
+    while (!cleanup_tasks.empty())
+      cleanup_tasks.pop();
+  }
 }
 
 Time TestMockTimeTaskRunner::Now() const {
@@ -341,8 +384,9 @@
   // unit tests. Make sure this TestMockTimeTaskRunner's tasks run in its scope.
   ScopedClosureRunner undo_override;
   if (!ThreadTaskRunnerHandle::IsSet() ||
-      ThreadTaskRunnerHandle::Get() != this) {
-    undo_override = ThreadTaskRunnerHandle::OverrideForTesting(this);
+      ThreadTaskRunnerHandle::Get() != proxy_task_runner_.get()) {
+    undo_override =
+        ThreadTaskRunnerHandle::OverrideForTesting(proxy_task_runner_.get());
   }
 
   const TimeTicks original_now_ticks = NowTicks();
diff --git a/base/test/test_mock_time_task_runner.h b/base/test/test_mock_time_task_runner.h
index 23dbb2f..dd7274c 100644
--- a/base/test/test_mock_time_task_runner.h
+++ b/base/test/test_mock_time_task_runner.h
@@ -205,6 +205,8 @@
   virtual void OnAfterTaskRun();
 
  private:
+  class NonOwningProxyTaskRunner;
+
   // MockClock implements TickClock and Clock. Always returns the then-current
   // mock time of |task_runner| as the current time or time ticks.
   class MockClock : public TickClock, public Clock {
@@ -277,6 +279,8 @@
 
   mutable Lock tasks_lock_;
   ConditionVariable tasks_lock_cv_;
+
+  const scoped_refptr<NonOwningProxyTaskRunner> proxy_task_runner_;
   std::unique_ptr<ThreadTaskRunnerHandle> thread_task_runner_handle_;
 
   // Set to true in RunLoop::Delegate::Quit() to signal the topmost
diff --git a/base/test/test_mock_time_task_runner_unittest.cc b/base/test/test_mock_time_task_runner_unittest.cc
index 04be466..299a6fa 100644
--- a/base/test/test_mock_time_task_runner_unittest.cc
+++ b/base/test/test_mock_time_task_runner_unittest.cc
@@ -7,6 +7,7 @@
 #include "base/cancelable_callback.h"
 #include "base/memory/ref_counted.h"
 #include "base/run_loop.h"
+#include "base/test/bind_test_util.h"
 #include "base/test/gtest_util.h"
 #include "base/test/test_timeouts.h"
 #include "base/threading/sequenced_task_runner_handle.h"
@@ -181,6 +182,33 @@
   EXPECT_EQ(expected_value, counter);
 }
 
+TEST(TestMockTimeTaskRunnerTest, AvoidCaptureWhenBound) {
+  // Make sure that capturing the active task runner --- which sometimes happens
+  // unknowingly due to ThreadsafeObserverList deep within some singleton ---
+  // does not keep the entire TestMockTimeTaskRunner alive, as in bound mode
+  // that's a RunLoop::Delegate, and leaking that renders any further tests that
+  // need RunLoop support unrunnable.
+  //
+  // (This used to happen when code run from ProcessAllTasksNoLaterThan grabbed
+  //  the runner.).
+  scoped_refptr<SingleThreadTaskRunner> captured;
+  {
+    auto task_runner = MakeRefCounted<TestMockTimeTaskRunner>(
+        TestMockTimeTaskRunner::Type::kBoundToThread);
+
+    task_runner->PostTask(FROM_HERE, base::BindLambdaForTesting([&]() {
+                            captured = ThreadTaskRunnerHandle::Get();
+                          }));
+    task_runner->RunUntilIdle();
+  }
+
+  {
+    // This should not complain about RunLoop::Delegate already existing.
+    auto task_runner2 = MakeRefCounted<TestMockTimeTaskRunner>(
+        TestMockTimeTaskRunner::Type::kBoundToThread);
+  }
+}
+
 // Regression test that receiving the quit-when-idle signal when already empty
 // works as intended (i.e. that |TestMockTimeTaskRunner::tasks_lock_cv| is
 // properly signaled).
diff --git a/build/config/android/config.gni b/build/config/android/config.gni
index 411215da..cf0ecabfc 100644
--- a/build/config/android/config.gni
+++ b/build/config/android/config.gni
@@ -23,7 +23,8 @@
                  ".gclient configuration file (in the parent directory of " +
                  "src) must include \"android\" and/or \"chromeos\". For " +
                  "example:${n}${n}solutions = [${n}...${n}]${n}" +
-                 "target_os=[\"chromeos\"]${n}")
+                 "target_os=[\"chromeos\"]${n}${n}" +
+                 "After adding |target_os| please re-run \"gclient sync\".${n}")
     }
   }
 
diff --git a/build/fuchsia/sdk.sha1 b/build/fuchsia/sdk.sha1
index a4673d44..96c9012 100644
--- a/build/fuchsia/sdk.sha1
+++ b/build/fuchsia/sdk.sha1
@@ -1 +1 @@
-3a07104afeb72fd5454fca8e0d48d5d7ee68e0c7
\ No newline at end of file
+4d52071a7689a36752023349a1927ad7a6de168c
diff --git a/cc/input/snap_fling_controller.h b/cc/input/snap_fling_controller.h
index 20f3056..5199b23f 100644
--- a/cc/input/snap_fling_controller.h
+++ b/cc/input/snap_fling_controller.h
@@ -24,8 +24,8 @@
 class SnapFlingClient {
  public:
   virtual bool GetSnapFlingInfo(const gfx::Vector2dF& natural_displacement,
-                                gfx::Vector2dF* out_initial_offset,
-                                gfx::Vector2dF* out_target_offset) const = 0;
+                                gfx::Vector2dF* initial_offset,
+                                gfx::Vector2dF* target_offset) const = 0;
   virtual gfx::Vector2dF ScrollByForSnapFling(const gfx::Vector2dF& delta) = 0;
   virtual void ScrollEndForSnapFling() = 0;
   virtual void RequestAnimationForSnapFling() = 0;
diff --git a/cc/trees/frame_rate_counter.cc b/cc/trees/frame_rate_counter.cc
index f801dbf6..ac4d517f 100644
--- a/cc/trees/frame_rate_counter.cc
+++ b/cc/trees/frame_rate_counter.cc
@@ -57,18 +57,6 @@
   base::TimeDelta frame_interval_seconds =
       RecentFrameInterval(ring_buffer_.BufferSize() - 1);
 
-  if (has_impl_thread_ && ring_buffer_.CurrentIndex() > 0) {
-    if (software) {
-      UMA_HISTOGRAM_CUSTOM_COUNTS(
-          "Renderer4.SoftwareCompositorThreadImplDrawDelay",
-          frame_interval_seconds.InMilliseconds(), 1, 120, 60);
-    } else {
-      UMA_HISTOGRAM_CUSTOM_COUNTS("Renderer4.CompositorThreadImplDrawDelay",
-                                  frame_interval_seconds.InMilliseconds(), 1,
-                                  120, 60);
-    }
-  }
-
   if (!IsBadFrameInterval(frame_interval_seconds) &&
       frame_interval_seconds.InSecondsF() > kDroppedFrameTime)
     dropped_frame_count_ +=
diff --git a/cc/trees/layer_tree_host_impl.cc b/cc/trees/layer_tree_host_impl.cc
index d5e12239..b9cd7da 100644
--- a/cc/trees/layer_tree_host_impl.cc
+++ b/cc/trees/layer_tree_host_impl.cc
@@ -4354,8 +4354,8 @@
 
 bool LayerTreeHostImpl::GetSnapFlingInfo(
     const gfx::Vector2dF& natural_displacement_in_viewport,
-    gfx::Vector2dF* out_initial_offset,
-    gfx::Vector2dF* out_target_offset) const {
+    gfx::Vector2dF* initial_offset,
+    gfx::Vector2dF* target_offset) const {
   const ScrollNode* scroll_node = CurrentlyScrollingNode();
   if (!scroll_node || !scroll_node->snap_container_data.has_value())
     return false;
@@ -4365,7 +4365,7 @@
   gfx::Vector2dF natural_displacement_in_content =
       gfx::ScaleVector2d(natural_displacement_in_viewport, 1.f / scale_factor);
 
-  *out_initial_offset =
+  *initial_offset =
       ScrollOffsetToVector2dF(GetVisualScrollOffset(*scroll_node));
 
   bool did_scroll_x = did_scroll_x_for_scroll_gesture_ ||
@@ -4374,15 +4374,15 @@
                       natural_displacement_in_content.y() != 0;
 
   gfx::ScrollOffset snap_offset;
-  if (!data.FindSnapPosition(gfx::ScrollOffset(*out_initial_offset +
-                                               natural_displacement_in_content),
-                             did_scroll_x, did_scroll_y, &snap_offset)) {
+  if (!data.FindSnapPosition(
+          gfx::ScrollOffset(*initial_offset + natural_displacement_in_content),
+          did_scroll_x, did_scroll_y, &snap_offset)) {
     return false;
   }
 
-  *out_target_offset = ScrollOffsetToVector2dF(snap_offset);
-  out_target_offset->Scale(scale_factor);
-  out_initial_offset->Scale(scale_factor);
+  *target_offset = ScrollOffsetToVector2dF(snap_offset);
+  target_offset->Scale(scale_factor);
+  initial_offset->Scale(scale_factor);
   return true;
 }
 
diff --git a/cc/trees/layer_tree_host_impl.h b/cc/trees/layer_tree_host_impl.h
index bc80fc9..dcdfa51 100644
--- a/cc/trees/layer_tree_host_impl.h
+++ b/cc/trees/layer_tree_host_impl.h
@@ -615,8 +615,8 @@
   gfx::ScrollOffset GetVisualScrollOffset(const ScrollNode& scroll_node) const;
 
   bool GetSnapFlingInfo(const gfx::Vector2dF& natural_displacement_in_viewport,
-                        gfx::Vector2dF* out_initial_offset,
-                        gfx::Vector2dF* out_target_offset) const override;
+                        gfx::Vector2dF* initial_offset,
+                        gfx::Vector2dF* target_offset) const override;
 
   // Returns the amount of delta that can be applied to scroll_node, taking
   // page scale into account.
diff --git a/chrome/android/java/res/layout/textbubble_text.xml b/chrome/android/java/res/layout/textbubble_text.xml
index 1c93c64..90f3edd 100644
--- a/chrome/android/java/res/layout/textbubble_text.xml
+++ b/chrome/android/java/res/layout/textbubble_text.xml
@@ -8,4 +8,4 @@
     android:layout_width="wrap_content"
     android:layout_height="wrap_content"
     android:padding="16dp"
-    android:textAppearance="@style/WhiteBody" />
+    android:textAppearance="@style/WhiteTitle2" />
diff --git a/chrome/android/java/res/layout/textbubble_text_with_image.xml b/chrome/android/java/res/layout/textbubble_text_with_image.xml
index 207225a..2f12ec6 100644
--- a/chrome/android/java/res/layout/textbubble_text_with_image.xml
+++ b/chrome/android/java/res/layout/textbubble_text_with_image.xml
@@ -29,6 +29,6 @@
         android:id="@+id/message"
         android:layout_width="wrap_content"
         android:layout_height="wrap_content"
-        android:textAppearance="@style/WhiteBody" />
+        android:textAppearance="@style/WhiteTitle2" />
 
 </LinearLayout>
\ No newline at end of file
diff --git a/chrome/android/java/res/layout/toolbar_phone_common.xml b/chrome/android/java/res/layout/toolbar_phone_common.xml
index 21c6dd4..86438ea1 100644
--- a/chrome/android/java/res/layout/toolbar_phone_common.xml
+++ b/chrome/android/java/res/layout/toolbar_phone_common.xml
@@ -46,7 +46,7 @@
 
         <ViewStub
             android:id="@+id/experimental_button_stub"
-            android:inflatedId="@+id/experimental_button"
+            android:inflatedId="@+id/experimental_toolbar_button"
             android:layout="@layout/experimental_toolbar_button"
             style="@style/ToolbarButton"
             android:layout_gravity="top"
diff --git a/chrome/android/java/res/values/dimens.xml b/chrome/android/java/res/values/dimens.xml
index b70ea66..4bd2cf4b 100644
--- a/chrome/android/java/res/values/dimens.xml
+++ b/chrome/android/java/res/values/dimens.xml
@@ -519,7 +519,7 @@
 
     <!-- TextBubble dimensions -->
     <dimen name="text_bubble_margin">4dp</dimen>
-    <dimen name="text_bubble_corner_radius">2dp</dimen>
+    <dimen name="text_bubble_corner_radius">8dp</dimen>
     <dimen name="text_bubble_arrow_width">20dp</dimen>
     <dimen name="text_bubble_arrow_height">10dp</dimen>
     <dimen name="text_bubble_menu_anchor_y_inset">14dp</dimen>
diff --git a/chrome/android/java/res/xml/sync_and_services_preferences.xml b/chrome/android/java/res/xml/sync_and_services_preferences.xml
index b0e6615..7a9a8320 100644
--- a/chrome/android/java/res/xml/sync_and_services_preferences.xml
+++ b/chrome/android/java/res/xml/sync_and_services_preferences.xml
@@ -27,16 +27,25 @@
         android:summary="@string/prefs_nonpersonalized_services_section_summary">
 
         <org.chromium.chrome.browser.preferences.ChromeBaseCheckBoxPreference
-            android:key="navigation_error"
-            android:title="@string/navigation_error_title"
-            android:summary="@string/navigation_error_summary"
+            android:key="search_suggestions"
+            android:title="@string/autocomplete_searches_and_urls_title"
+            android:summary="@string/autocomplete_searches_and_urls_summary"
             android:defaultValue="true"/>
         <org.chromium.chrome.browser.preferences.ChromeBaseCheckBoxPreference
-            android:key="search_suggestions"
-            android:title="@string/search_suggestions_title"
-            android:summary="@string/search_suggestions_summary"
+            android:key="network_predictions"
+            android:title="@string/preload_pages_title"
+            android:summary="@string/preload_pages_summary"
+            android:persistent="false"/>
+        <org.chromium.chrome.browser.preferences.ChromeBaseCheckBoxPreference
+            android:key="navigation_error"
+            android:title="@string/navigation_error_suggestions_title"
+            android:summary="@string/navigation_error_suggestions_summary"
             android:defaultValue="true"/>
         <org.chromium.chrome.browser.preferences.ChromeBaseCheckBoxPreference
+            android:key="safe_browsing"
+            android:title="@string/safe_browsing_title"
+            android:summary="@string/safe_browsing_summary"/>
+        <org.chromium.chrome.browser.preferences.ChromeBaseCheckBoxPreference
             android:key="safe_browsing_extended_reporting"
             android:title="@string/safe_browsing_extended_reporting_title"
             android:summary="@string/safe_browsing_extended_reporting_summary"/>
@@ -44,19 +53,15 @@
             android:key="safe_browsing_scout_reporting"
             android:title="@string/safe_browsing_scout_reporting_title"
             android:summary="@string/safe_browsing_scout_reporting_summary"/>
-        <org.chromium.chrome.browser.preferences.ChromeBaseCheckBoxPreference
-            android:key="safe_browsing"
-            android:title="@string/safe_browsing_title"
-            android:summary="@string/safe_browsing_summary"/>
-        <org.chromium.chrome.browser.preferences.ChromeBaseCheckBoxPreference
-            android:key="network_predictions"
-            android:title="@string/network_predictions_title"
-            android:summary="@string/network_predictions_summary"
-            android:persistent="false"/>
         <org.chromium.chrome.browser.preferences.ChromeBasePreference
             android:key="usage_and_crash_reports"
             android:title="@string/usage_and_crash_reports_title"
             android:fragment="org.chromium.chrome.browser.preferences.privacy.UsageAndCrashReportsPreferenceFragment"/>
+        <org.chromium.chrome.browser.preferences.ChromeBaseCheckBoxPreference
+            android:key="url_keyed_anonymized_data"
+            android:title="@string/url_keyed_anonymized_data_title"
+            android:summary="@string/url_keyed_anonymized_data_summary"
+            android:persistent="false"/>
         <org.chromium.chrome.browser.preferences.ChromeBasePreference
             android:key="contextual_search"
             android:title="@string/contextual_search_title"
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/contextual_suggestions/ContextualSuggestionsMediator.java b/chrome/android/java/src/org/chromium/chrome/browser/contextual_suggestions/ContextualSuggestionsMediator.java
index aca068a..ae9edc2 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/contextual_suggestions/ContextualSuggestionsMediator.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/contextual_suggestions/ContextualSuggestionsMediator.java
@@ -319,6 +319,7 @@
         reportEvent(ContextualSuggestionsEvent.UI_BUTTON_SHOWN);
         TrackerFactory.getTrackerForProfile(mProfile).notifyEvent(
                 EventConstants.CONTEXTUAL_SUGGESTIONS_BUTTON_SHOWN);
+        maybeShowHelpBubble();
     }
 
     @Override
@@ -541,22 +542,35 @@
             return;
         }
 
-        int extraInset = mModel.isSlimPeekEnabled()
-                ? mIphParentView.getResources().getDimensionPixelSize(
-                          R.dimen.contextual_suggestions_slim_peek_inset)
-                : 0;
+        ViewRectProvider rectProvider;
+        if (!mToolbarButtonEnabled) {
+            int extraInset = mModel.isSlimPeekEnabled()
+                    ? mIphParentView.getResources().getDimensionPixelSize(
+                              R.dimen.contextual_suggestions_slim_peek_inset)
+                    : 0;
 
-        ViewRectProvider rectProvider = new ViewRectProvider(mIphParentView);
-        rectProvider.setInsetPx(0,
-                mIphParentView.getResources().getDimensionPixelSize(R.dimen.toolbar_shadow_height)
-                        + extraInset,
-                0, 0);
-        if (mModel.isSlimPeekEnabled()) {
+            rectProvider = new ViewRectProvider(mIphParentView);
+            rectProvider.setInsetPx(0,
+                    mIphParentView.getResources().getDimensionPixelSize(
+                            R.dimen.toolbar_shadow_height)
+                            + extraInset,
+                    0, 0);
+        } else {
+            rectProvider = new ViewRectProvider(
+                    mIphParentView.getRootView().findViewById(R.id.experimental_toolbar_button));
+            rectProvider.setInsetPx(0, 0, 0,
+                    mIphParentView.getResources().getDimensionPixelOffset(
+                            R.dimen.text_bubble_menu_anchor_y_inset));
+        }
+
+        if (mModel.isSlimPeekEnabled() || mToolbarButtonEnabled) {
             mHelpBubble = new ImageTextBubble(mIphParentView.getContext(), mIphParentView,
                     R.string.contextual_suggestions_in_product_help,
                     R.string.contextual_suggestions_in_product_help, true, rectProvider,
                     R.drawable.ic_logo_googleg_24dp);
-            mModel.setToolbarArrowTintResourceId(R.color.default_icon_color_blue);
+            if (!mToolbarButtonEnabled) {
+                mModel.setToolbarArrowTintResourceId(R.color.default_icon_color_blue);
+            }
         } else {
             mHelpBubble = new TextBubble(mIphParentView.getContext(), mIphParentView,
                     R.string.contextual_suggestions_in_product_help,
@@ -567,7 +581,9 @@
         mHelpBubble.addOnDismissListener(() -> {
             tracker.dismissed(FeatureConstants.CONTEXTUAL_SUGGESTIONS_FEATURE);
             mHelpBubble = null;
-            mModel.setToolbarArrowTintResourceId(R.color.dark_mode_tint);
+            if (!mToolbarButtonEnabled) {
+                mModel.setToolbarArrowTintResourceId(R.color.dark_mode_tint);
+            }
         });
 
         mHelpBubble.show();
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/customtabs/CustomTabActivity.java b/chrome/android/java/src/org/chromium/chrome/browser/customtabs/CustomTabActivity.java
index ae92b47..3dccd16 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/customtabs/CustomTabActivity.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/customtabs/CustomTabActivity.java
@@ -62,6 +62,7 @@
 import org.chromium.chrome.browser.customtabs.dynamicmodule.ActivityDelegate;
 import org.chromium.chrome.browser.customtabs.dynamicmodule.ActivityHostImpl;
 import org.chromium.chrome.browser.customtabs.dynamicmodule.ModuleEntryPoint;
+import org.chromium.chrome.browser.customtabs.dynamicmodule.ModuleMetrics;
 import org.chromium.chrome.browser.datausage.DataUseTabUIManager;
 import org.chromium.chrome.browser.externalauth.ExternalAuthUtils;
 import org.chromium.chrome.browser.externalnav.ExternalNavigationDelegateImpl;
@@ -371,7 +372,9 @@
         ModuleEntryPoint entryPoint = mConnection.loadModule(packageName, className);
         if (entryPoint == null) return;
 
+        long createActivityDelegateStartTime = ModuleMetrics.now();
         mActivityDelegate = entryPoint.createActivityDelegate(new ActivityHostImpl(this));
+        ModuleMetrics.recordCreateActivityDelegateTime(createActivityDelegateStartTime);
         mActivityDelegate.onCreate(getSavedInstanceState());
     }
 
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/customtabs/dynamicmodule/ModuleLoader.java b/chrome/android/java/src/org/chromium/chrome/browser/customtabs/dynamicmodule/ModuleLoader.java
index 2cf7da5a..7c4b7de5 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/customtabs/dynamicmodule/ModuleLoader.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/customtabs/dynamicmodule/ModuleLoader.java
@@ -36,9 +36,13 @@
         if (moduleContext == null) return null;
 
         try {
+            long entryPointLoadClassStartTime = ModuleMetrics.now();
             Class<?> clazz = moduleContext.getClassLoader().loadClass(className);
+            ModuleMetrics.recordLoadClassTime(entryPointLoadClassStartTime);
 
+            long entryPointNewInstanceStartTime = ModuleMetrics.now();
             IBinder binder = (IBinder) clazz.newInstance();
+            ModuleMetrics.recordEntryPointNewInstanceTime(entryPointNewInstanceStartTime);
 
             ModuleHostImpl moduleHost = new ModuleHostImpl(applicationContext, moduleContext);
             ModuleEntryPoint entryPoint =
@@ -67,8 +71,10 @@
         try {
             // The flags Context.CONTEXT_INCLUDE_CODE and Context.CONTEXT_IGNORE_SECURITY are
             // needed to be able to load classes via the classloader of the returned context.
+            long createPackageContextStartTime = ModuleMetrics.now();
             Context moduleContext = applicationContext.createPackageContext(
                     packageName, Context.CONTEXT_INCLUDE_CODE | Context.CONTEXT_IGNORE_SECURITY);
+            ModuleMetrics.recordCreatePackageContextTime(createPackageContextStartTime);
             return moduleContext;
         } catch (PackageManager.NameNotFoundException e) {
             Log.e(TAG, "Could not create package context for %s", packageName, e);
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/customtabs/dynamicmodule/ModuleMetrics.java b/chrome/android/java/src/org/chromium/chrome/browser/customtabs/dynamicmodule/ModuleMetrics.java
new file mode 100644
index 0000000..30e8e39
--- /dev/null
+++ b/chrome/android/java/src/org/chromium/chrome/browser/customtabs/dynamicmodule/ModuleMetrics.java
@@ -0,0 +1,52 @@
+// 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.
+
+package org.chromium.chrome.browser.customtabs.dynamicmodule;
+
+import android.os.SystemClock;
+
+import org.chromium.base.metrics.RecordHistogram;
+
+import java.util.concurrent.TimeUnit;
+
+/**
+ * Records metrics related to custom tabs dynamic modules.
+ */
+public final class ModuleMetrics {
+    private ModuleMetrics() {}
+
+    /**
+     * SystemClock.uptimeMillis() is used here as it uses the same system call as all the native
+     * side of Chrome, and this is the same clock used for page load metrics.
+     *
+     * @return Milliseconds since boot, not counting time spent in deep sleep.
+     */
+    public static long now() {
+        return SystemClock.uptimeMillis();
+    }
+
+    public static void recordCreateActivityDelegateTime(long startTime) {
+        RecordHistogram.recordMediumTimesHistogram(
+                "CustomTabs.DynamicModule.CreateActivityDelegateTime", now() - startTime,
+                TimeUnit.MILLISECONDS);
+    }
+
+    public static void recordCreatePackageContextTime(long startTime) {
+        RecordHistogram.recordMediumTimesHistogram(
+                "CustomTabs.DynamicModule.CreatePackageContextTime", now() - startTime,
+                TimeUnit.MILLISECONDS);
+    }
+
+    public static void recordLoadClassTime(long startTime) {
+        RecordHistogram.recordMediumTimesHistogram(
+                "CustomTabs.DynamicModule.EntryPointLoadClassTime", now() - startTime,
+                TimeUnit.MILLISECONDS);
+    }
+
+    public static void recordEntryPointNewInstanceTime(long startTime) {
+        RecordHistogram.recordMediumTimesHistogram(
+                "CustomTabs.DynamicModule.EntryPointNewInstanceTime", now() - startTime,
+                TimeUnit.MILLISECONDS);
+    }
+}
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/media/ui/MediaNotificationManager.java b/chrome/android/java/src/org/chromium/chrome/browser/media/ui/MediaNotificationManager.java
index cd978d6d..c18c227 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/media/ui/MediaNotificationManager.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/media/ui/MediaNotificationManager.java
@@ -354,6 +354,8 @@
 
         @VisibleForTesting
         void stopListenerService() {
+            // Call stopForeground to guarantee  Android unset the foreground bit.
+            stopForeground(true /* removeNotification */);
             stopSelf();
         }
 
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/preferences/SyncAndServicesPreferences.java b/chrome/android/java/src/org/chromium/chrome/browser/preferences/SyncAndServicesPreferences.java
index 5f509235..cda1684 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/preferences/SyncAndServicesPreferences.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/preferences/SyncAndServicesPreferences.java
@@ -16,7 +16,6 @@
 
 import org.chromium.base.metrics.RecordHistogram;
 import org.chromium.chrome.R;
-import org.chromium.chrome.browser.ChromeFeatureList;
 import org.chromium.chrome.browser.contextualsearch.ContextualSearchFieldTrial;
 import org.chromium.chrome.browser.help.HelpAndFeedback;
 import org.chromium.chrome.browser.preferences.privacy.PrivacyPreferencesManager;
@@ -95,10 +94,6 @@
         mSearchSuggestions = (ChromeBaseCheckBoxPreference) findPreference(PREF_SEARCH_SUGGESTIONS);
         mSearchSuggestions.setOnPreferenceChangeListener(this);
         mSearchSuggestions.setManagedPreferenceDelegate(mManagedPreferenceDelegate);
-        if (ChromeFeatureList.isEnabled(ChromeFeatureList.CONTENT_SUGGESTIONS_SETTINGS)) {
-            mSearchSuggestions.setTitle(R.string.search_site_suggestions_title);
-            mSearchSuggestions.setSummary(R.string.search_site_suggestions_summary);
-        }
 
         mContextualSearch = findPreference(PREF_CONTEXTUAL_SEARCH);
         if (!ContextualSearchFieldTrial.isEnabled()) {
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/toolbar/ToolbarPhone.java b/chrome/android/java/src/org/chromium/chrome/browser/toolbar/ToolbarPhone.java
index 14d0fff..f7960e8 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/toolbar/ToolbarPhone.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/toolbar/ToolbarPhone.java
@@ -2785,6 +2785,11 @@
         if (mLayoutUpdateHost != null) mLayoutUpdateHost.requestUpdate();
     }
 
+    @VisibleForTesting
+    public View getExperimentalButtonForTesting() {
+        return mExperimentalButton;
+    }
+
     private void setTabSwitcherAnimationMenuDrawable() {
         mTabSwitcherAnimationMenuDrawable = ApiCompatibilityUtils.getDrawable(
                 getResources(), R.drawable.ic_more_vert_black_24dp);
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/vr_shell/VrDialogManager.java b/chrome/android/java/src/org/chromium/chrome/browser/vr_shell/VrDialogManager.java
index 0e1bf3d..6437238 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/vr_shell/VrDialogManager.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/vr_shell/VrDialogManager.java
@@ -54,4 +54,14 @@
      * @param floating indicates if the dialog is floating.
      */
     void setDialogFloating(boolean floating);
+
+    /**
+     * Set dismiss handler for dialog. When VrDialogManager wants to close the
+     * dialog should call dismissHandler. This is used to close the dialog when
+     * native wants to dismiss.
+     *
+     * @param dismissHandler is called when VrDialogManager wants to dismiss the
+     * current dialog.
+     */
+    void setVrDialogDismissHandler(Runnable dismissHandler);
 }
\ No newline at end of file
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/vr_shell/VrPopupWindow.java b/chrome/android/java/src/org/chromium/chrome/browser/vr_shell/VrPopupWindow.java
index c2584cfd..07a2b80 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/vr_shell/VrPopupWindow.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/vr_shell/VrPopupWindow.java
@@ -46,6 +46,13 @@
         mVrDialogManager.setDialogView(mVrPopupContainer);
         mVrDialogManager.initVrDialog(mWidth, mHeight);
         mVrDialogManager.setDialogLocation(x, y);
+        mVrDialogManager.setVrDialogDismissHandler(new Runnable() {
+            @Override
+            public void run() {
+                dismiss();
+            }
+        });
+
         mIsShowing = true;
     }
 
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/vr_shell/VrShellImpl.java b/chrome/android/java/src/org/chromium/chrome/browser/vr_shell/VrShellImpl.java
index 0b3bbd2..383ba3d 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/vr_shell/VrShellImpl.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/vr_shell/VrShellImpl.java
@@ -126,6 +126,7 @@
     private ModalDialogManager mNonVrModalDialogManager;
     private ModalDialogManager mVrModalDialogManager;
     private VrModalPresenter mVrModalPresenter;
+    private Runnable mVrDialogDismissHandler;
 
     private VrInputMethodManagerWrapper mInputMethodManagerWrapper;
 
@@ -587,6 +588,18 @@
     @CalledByNative
     public void closeCurrentDialog() {
         mVrModalPresenter.closeCurrentDialog();
+        if (mVrDialogDismissHandler != null) {
+            mVrDialogDismissHandler.run();
+            mVrDialogDismissHandler = null;
+        }
+    }
+
+    /**
+     * @param vrDialogDismissHandler the mVrDialogDismissHandler to set
+     */
+    @Override
+    public void setVrDialogDismissHandler(Runnable vrDialogDismissHandler) {
+        mVrDialogDismissHandler = vrDialogDismissHandler;
     }
 
     @Override
@@ -796,6 +809,7 @@
     public void closeVrDialog() {
         nativeCloseAlertDialog(mNativeVrShell);
         mVrUiViewContainer.removeAllViews();
+        mVrDialogDismissHandler = null;
     }
 
     /**
diff --git a/chrome/android/java/strings/android_chrome_strings.grd b/chrome/android/java/strings/android_chrome_strings.grd
index ca2c3b6..746480e 100644
--- a/chrome/android/java/strings/android_chrome_strings.grd
+++ b/chrome/android/java/strings/android_chrome_strings.grd
@@ -325,6 +325,30 @@
       <message name="IDS_PREFS_NONPERSONALIZED_SERVICES_SECTION_SUMMARY" desc="Summary for the expandable group of preferences that control non-personalized Google services. This group contains preferences for data that is not tied to user's Google Account.">
         Communicates with Google to improve browsing and Chrome
       </message>
+      <message name="IDS_AUTOCOMPLETE_SEARCHES_AND_URLS_TITLE" desc="Title for a checkbox in Settings that controls URL and search autocompletion and informs the user about the data shared by this feature.">
+        Autocomplete searches and URLs
+      </message>
+      <message name="IDS_AUTOCOMPLETE_SEARCHES_AND_URLS_SUMMARY" desc="Summary for a checkbox in Settings that controls URL and search autocompletion and informs the user about the data shared by this feature.">
+        Sends searches from the address bar and search box, and some cookies to your default search engine
+      </message>
+      <message name="IDS_PRELOAD_PAGES_TITLE" desc="Title for a checkbox in Settings that controls pages preloading and informs the user about the data shared by this feature.">
+        Preload pages for faster browsing and searching
+      </message>
+      <message name="IDS_PRELOAD_PAGES_SUMMARY" desc="Summary for a checkbox in Settings that controls pages preloading and informs the user about the data shared by this feature.">
+        Uses cookies to remember your preferences, even if you don't visit those pages
+      </message>
+      <message name="IDS_NAVIGATION_ERROR_SUGGESTIONS_TITLE" desc="Title for a checkbox in Settings that controls pages suggestions on navigation errors and informs the user about the data shared by this feature.">
+        Shows suggestions for similar pages when a page can't be found
+      </message>
+      <message name="IDS_NAVIGATION_ERROR_SUGGESTIONS_SUMMARY" desc="Summary for a checkbox in Settings that controls pages suggestions on navigation errors and informs the user about the data shared by this feature.">
+        Sends the URL of a page you're trying to reach to Google
+      </message>
+      <message name="IDS_URL_KEYED_ANONYMIZED_DATA_TITLE" desc="Title for a checkbox in Settings that controls non-personalized URL collection and informs the user about the data shared by this feature.">
+        Make searches and browsing better
+      </message>
+      <message name="IDS_URL_KEYED_ANONYMIZED_DATA_SUMMARY" desc="Summary for a checkbox in Settings that controls non-personalized URL collection and informs the user about the data shared by this feature.">
+        Sends URLs of pages you visit to Google
+      </message>
 
       <!-- Search engine preferences -->
       <message name="IDS_PREFS_SEARCH_ENGINE" desc="Title for Search Engine preferences. [CHAR-LIMIT=32]">
diff --git a/chrome/android/java_sources.gni b/chrome/android/java_sources.gni
index 2b862cd..5351ff8 100644
--- a/chrome/android/java_sources.gni
+++ b/chrome/android/java_sources.gni
@@ -383,6 +383,7 @@
   "java/src/org/chromium/chrome/browser/customtabs/dynamicmodule/ModuleEntryPoint.java",
   "java/src/org/chromium/chrome/browser/customtabs/dynamicmodule/ModuleHostImpl.java",
   "java/src/org/chromium/chrome/browser/customtabs/dynamicmodule/ModuleLoader.java",
+  "java/src/org/chromium/chrome/browser/customtabs/dynamicmodule/ModuleMetrics.java",
   "java/src/org/chromium/chrome/browser/customtabs/dynamicmodule/ObjectWrapper.java",
   "java/src/org/chromium/chrome/browser/database/SQLiteCursor.java",
   "java/src/org/chromium/chrome/browser/datausage/DataUseTabUIManager.java",
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/contextual_suggestions/ContextualSuggestionsTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/contextual_suggestions/ContextualSuggestionsTest.java
index 3bbd73f..2b6645fc4 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/contextual_suggestions/ContextualSuggestionsTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/contextual_suggestions/ContextualSuggestionsTest.java
@@ -53,7 +53,10 @@
 import org.chromium.chrome.browser.ntp.ContextMenuManager;
 import org.chromium.chrome.browser.ntp.cards.NewTabPageViewHolder.PartialBindCallback;
 import org.chromium.chrome.browser.ntp.snippets.SnippetArticleViewHolder;
+import org.chromium.chrome.browser.tabmodel.TabModel;
+import org.chromium.chrome.browser.tabmodel.TabModel.TabSelectionType;
 import org.chromium.chrome.browser.test.ScreenShooter;
+import org.chromium.chrome.browser.toolbar.ToolbarPhone;
 import org.chromium.chrome.browser.widget.bottomsheet.BottomSheet;
 import org.chromium.chrome.browser.widget.bottomsheet.EmptyBottomSheetObserver;
 import org.chromium.chrome.test.BottomSheetTestRule;
@@ -81,6 +84,7 @@
 
 import java.util.Arrays;
 import java.util.Locale;
+import java.util.concurrent.ExecutionException;
 import java.util.concurrent.TimeoutException;
 
 /**
@@ -328,26 +332,7 @@
     public void testOpenSuggestion() throws InterruptedException, TimeoutException {
         forceShowSuggestions();
         openSheet();
-
-        SnippetArticleViewHolder holder = getFirstSuggestionViewHolder();
-        String expectedUrl = holder.getUrl();
-
-        TestWebContentsObserver webContentsObserver = new TestWebContentsObserver(
-                mActivityTestRule.getActivity().getActivityTab().getWebContents());
-
-        int callCount = webContentsObserver.getOnPageStartedHelper().getCallCount();
-
-        ThreadUtils.runOnUiThreadBlocking(() -> {
-            holder.itemView.performClick();
-        });
-
-        webContentsObserver.getOnPageStartedHelper().waitForCallback(callCount);
-
-        ThreadUtils.runOnUiThreadBlocking(() -> mBottomSheet.endAnimations());
-
-        assertEquals("Tab URL should match snippet URL", expectedUrl,
-                mActivityTestRule.getActivity().getActivityTab().getUrl());
-        assertFalse("Sheet should be closed.", mBottomSheet.isSheetOpen());
+        testOpenFirstSuggestion();
     }
 
     @Test
@@ -741,6 +726,78 @@
         assertNull("Bottom sheet contents should be null.", mBottomSheet.getCurrentSheetContent());
     }
 
+    @Test
+    @MediumTest
+    @Feature({"ContextualSuggestions"})
+    @EnableFeatures(ChromeFeatureList.CONTEXTUAL_SUGGESTIONS_BUTTON)
+    @DisableFeatures(ChromeFeatureList.CONTEXTUAL_SUGGESTIONS_BOTTOM_SHEET)
+    public void testToolbarButton() throws Exception {
+        View toolbarButton = getToolbarButton();
+        assertEquals(
+                "Toolbar button should be visible", View.VISIBLE, toolbarButton.getVisibility());
+
+        clickToolbarButton();
+        simulateClickOnCloseButton();
+
+        assertEquals(
+                "Toolbar button should be visible", View.VISIBLE, toolbarButton.getVisibility());
+
+        clickToolbarButton();
+        testOpenFirstSuggestion();
+
+        assertEquals("Toolbar button should be visible", View.GONE, toolbarButton.getVisibility());
+    }
+
+    @Test
+    @MediumTest
+    @Feature({"ContextualSuggestions"})
+    @EnableFeatures(ChromeFeatureList.CONTEXTUAL_SUGGESTIONS_BUTTON)
+    @DisableFeatures(ChromeFeatureList.CONTEXTUAL_SUGGESTIONS_BOTTOM_SHEET)
+    public void testToolbarButton_ToggleTabSwitcher() throws Exception {
+        View toolbarButton = getToolbarButton();
+
+        assertEquals(
+                "Toolbar button should be visible", View.VISIBLE, toolbarButton.getVisibility());
+
+        ThreadUtils.runOnUiThreadBlocking(
+                () -> { mActivityTestRule.getActivity().getLayoutManager().showOverview(false); });
+
+        assertEquals("Toolbar button should be invisible", View.INVISIBLE,
+                toolbarButton.getVisibility());
+
+        ThreadUtils.runOnUiThreadBlocking(
+                () -> { mActivityTestRule.getActivity().getLayoutManager().hideOverview(false); });
+
+        assertEquals(
+                "Toolbar button should be visible", View.VISIBLE, toolbarButton.getVisibility());
+    }
+
+    @Test
+    @MediumTest
+    @Feature({"ContextualSuggestions"})
+    @EnableFeatures(ChromeFeatureList.CONTEXTUAL_SUGGESTIONS_BUTTON)
+    @DisableFeatures(ChromeFeatureList.CONTEXTUAL_SUGGESTIONS_BOTTOM_SHEET)
+    public void testToolbarButton_SwitchTabs() throws Exception {
+        View toolbarButton = getToolbarButton();
+
+        assertEquals(
+                "Toolbar button should be visible", View.VISIBLE, toolbarButton.getVisibility());
+
+        final TabModel currentModel =
+                mActivityTestRule.getActivity().getTabModelSelector().getCurrentModel();
+        int currentIndex = currentModel.index();
+        ChromeTabUtils.newTabFromMenu(
+                InstrumentationRegistry.getInstrumentation(), mActivityTestRule.getActivity());
+
+        assertEquals("Toolbar button should be gone", View.GONE, toolbarButton.getVisibility());
+
+        ThreadUtils.runOnUiThreadBlocking(
+                () -> currentModel.setIndex(currentIndex, TabSelectionType.FROM_USER));
+
+        CriteriaHelper.pollUiThread(
+                () -> { return toolbarButton.getVisibility() == View.VISIBLE; });
+    }
+
     private void forceShowSuggestions() throws InterruptedException, TimeoutException {
         assertEquals("Model has incorrect number of items.",
                 (int) FakeContextualSuggestionsSource.TOTAL_ITEM_COUNT,
@@ -806,4 +863,45 @@
 
         return (SnippetArticleViewHolder) recyclerView.findViewHolderForAdapterPosition(index);
     }
+
+    private View getToolbarButton() throws ExecutionException {
+        return ThreadUtils.runOnUiThreadBlocking(() -> {
+            return ((ToolbarPhone) mActivityTestRule.getActivity()
+                            .getToolbarManager()
+                            .getToolbarLayout())
+                    .getExperimentalButtonForTesting();
+        });
+    }
+
+    private void clickToolbarButton() throws ExecutionException {
+        View toolbarButton = getToolbarButton();
+        assertEquals(
+                "Toolbar button should be visible", View.VISIBLE, toolbarButton.getVisibility());
+
+        ThreadUtils.runOnUiThreadBlocking(() -> {
+            toolbarButton.performClick();
+            mBottomSheet.endAnimations();
+        });
+        assertTrue("Sheet should be open.", mBottomSheet.isSheetOpen());
+    }
+
+    private void testOpenFirstSuggestion() throws InterruptedException, TimeoutException {
+        SnippetArticleViewHolder holder = getFirstSuggestionViewHolder();
+        String expectedUrl = holder.getUrl();
+
+        TestWebContentsObserver webContentsObserver = new TestWebContentsObserver(
+                mActivityTestRule.getActivity().getActivityTab().getWebContents());
+
+        int callCount = webContentsObserver.getOnPageStartedHelper().getCallCount();
+
+        ThreadUtils.runOnUiThreadBlocking(() -> { holder.itemView.performClick(); });
+
+        webContentsObserver.getOnPageStartedHelper().waitForCallback(callCount);
+
+        ThreadUtils.runOnUiThreadBlocking(() -> mBottomSheet.endAnimations());
+
+        assertEquals("Tab URL should match snippet URL", expectedUrl,
+                mActivityTestRule.getActivity().getActivityTab().getUrl());
+        assertFalse("Sheet should be closed.", mBottomSheet.isSheetOpen());
+    }
 }
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/vr_shell/WebVrInputTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/vr_shell/WebVrInputTest.java
index d9bef45..77327f8 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/vr_shell/WebVrInputTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/vr_shell/WebVrInputTest.java
@@ -34,6 +34,7 @@
 import org.chromium.base.test.params.ParameterizedRunner;
 import org.chromium.base.test.util.CommandLineFlags;
 import org.chromium.base.test.util.DisableIf;
+import org.chromium.base.test.util.DisabledTest;
 import org.chromium.base.test.util.MinAndroidSdkLevel;
 import org.chromium.base.test.util.Restriction;
 import org.chromium.base.test.util.RetryOnFailure;
@@ -566,6 +567,7 @@
     /**
      * Tests that focus loss updates synchronously.
      */
+    @DisabledTest(message = "crbug.com/859666")
     @Test
     @MediumTest
     @Restriction(RESTRICTION_TYPE_VIEWER_DAYDREAM)
diff --git a/chrome/app/generated_resources.grd b/chrome/app/generated_resources.grd
index 57ca598..c75419652 100644
--- a/chrome/app/generated_resources.grd
+++ b/chrome/app/generated_resources.grd
@@ -10011,6 +10011,12 @@
     <message name="IDS_WEBAUTHN_USB_INSERT_DESCRIPTION" desc="Contents of the dialog shown instructing the user to plug in their USB security key.">
       Plug in your Security Key and activate it
     </message>
+    <message name="IDS_WEBAUTHN_TIMEOUT_TITLE" desc="Title of the dialog shown when the Web Authentication request times out.">
+      Time out
+    </message>
+    <message name="IDS_WEBAUTHN_TIMEOUT_DESCRIPTION" desc="Contents of the dialog shown when Web Authentication request times out due to inactivity or error.">
+      The request timed out
+    </message>
   </messages>
  </release>
 </grit>
diff --git a/chrome/browser/android/vr/vr_shell_gl.cc b/chrome/browser/android/vr/vr_shell_gl.cc
index 50d717d..6c46f8a 100644
--- a/chrome/browser/android/vr/vr_shell_gl.cc
+++ b/chrome/browser/android/vr/vr_shell_gl.cc
@@ -1511,14 +1511,15 @@
 
   // From this point on, the current frame is either a pure UI frame
   // (frame_index==-1), or a WebVR frame (frame_index >= 0). If it's a WebVR
-  // frame, it must be the current processing frame, and ShouldDrawWebVr() must
-  // be true (not in UI-only mode). Careful, we may still have a processing
-  // frame in UI mode that couldn't be cancelled yet. Also, WebVR frames
-  // can still have overlay UI drawn on top of them.
+  // frame, it must be the current processing frame. Careful, we may still have
+  // a processing frame in UI mode that couldn't be cancelled yet. For example
+  // when showing a permission prompt, ShouldDrawWebVr() may have become false
+  // in the time between SubmitFrame and OnWebVRFrameAvailable or
+  // OnWebVRTokenSignaled. In that case we continue handling the current frame
+  // as a WebVR frame. Also, WebVR frames can still have overlay UI drawn on top
+  // of them.
   bool is_webvr_frame = frame_index >= 0;
-  DCHECK_EQ(is_webvr_frame, ShouldDrawWebVr());
   DCHECK(!is_webvr_frame || webxr_->HaveProcessingFrame());
-
   CHECK(!acquired_frame_);
 
   // When using async reprojection, we need to know which pose was
@@ -1526,8 +1527,9 @@
   // submitting. Technically we don't need a pose if not reprojecting,
   // but keeping it uninitialized seems likely to cause problems down
   // the road. Copying it is cheaper than fetching a new one.
-  if (is_webvr_frame && webxr_->HaveProcessingFrame()) {
+  if (is_webvr_frame) {
     // Copy into render info for overlay UI. WebVR doesn't use this.
+    DCHECK(webxr_->HaveProcessingFrame());
     WebXrFrame* frame = webxr_->GetProcessingFrame();
     render_info_.head_pose = frame->head_pose;
   } else {
diff --git a/chrome/browser/chromeos/arc/arc_optin_uma.cc b/chrome/browser/chromeos/arc/arc_optin_uma.cc
index 50670fa..00e8af9a 100644
--- a/chrome/browser/chromeos/arc/arc_optin_uma.cc
+++ b/chrome/browser/chromeos/arc/arc_optin_uma.cc
@@ -9,6 +9,7 @@
 #include "base/metrics/histogram_functions.h"
 #include "base/metrics/histogram_macros.h"
 #include "chrome/browser/chromeos/arc/policy/arc_policy_util.h"
+#include "chrome/browser/chromeos/login/demo_mode/demo_session.h"
 #include "chrome/browser/profiles/profile.h"
 #include "components/arc/arc_util.h"
 
@@ -19,8 +20,14 @@
 // Adds a suffix to the name based on the account type.
 std::string GetHistogramName(const std::string& base_name,
                              const Profile* profile) {
-  if (IsRobotAccountMode())
+  if (IsRobotOrOfflineDemoAccountMode()) {
+    chromeos::DemoSession* demo_session = chromeos::DemoSession::Get();
+    if (demo_session && demo_session->started()) {
+      return demo_session->offline_enrolled() ? base_name + "OfflineDemoMode"
+                                              : base_name + "DemoMode";
+    }
     return base_name + "RobotAccount";
+  }
   if (profile->IsChild())
     return base_name + "Child";
   return base_name +
diff --git a/chrome/browser/chromeos/arc/arc_session_manager.cc b/chrome/browser/chromeos/arc/arc_session_manager.cc
index 64753d1c..37f62d2 100644
--- a/chrome/browser/chromeos/arc/arc_session_manager.cc
+++ b/chrome/browser/chromeos/arc/arc_session_manager.cc
@@ -117,7 +117,7 @@
   if (ShouldShowOptInForTesting())
     return true;
 
-  if (IsRobotAccountMode())
+  if (IsRobotOrOfflineDemoAccountMode())
     return false;
 
   if (IsArcOptInVerificationDisabled())
@@ -300,7 +300,7 @@
 
   if (result == ProvisioningResult::CHROME_SERVER_COMMUNICATION_ERROR) {
     // TODO(poromov): Consider PublicSession offline mode.
-    if (IsRobotAccountMode()) {
+    if (IsRobotOrOfflineDemoAccountMode()) {
       VLOG(1) << "Robot account auth code fetching error";
       // Log out the user. All the cleanup will be done in Shutdown() method.
       // The callback is not called because auth code is empty.
@@ -442,7 +442,7 @@
   // in typical use case there will be no one nearby the kiosk device, who can
   // do some action to solve the problem be means of UI.
   if (g_ui_enabled && !IsArcOptInVerificationDisabled() &&
-      !IsRobotAccountMode()) {
+      !IsRobotOrOfflineDemoAccountMode()) {
     DCHECK(!support_host_);
     support_host_ = std::make_unique<ArcSupportHost>(profile_);
     support_host_->SetErrorDelegate(this);
@@ -659,7 +659,7 @@
   // This is for testing purpose.
   const bool start_arc_directly =
       prefs->GetBoolean(prefs::kArcSignedIn) || ShouldArcAlwaysStart() ||
-      IsRobotAccountMode() || IsArcOptInVerificationDisabled();
+      IsRobotOrOfflineDemoAccountMode() || IsArcOptInVerificationDisabled();
 
   // When ARC is blocked because of filesystem compatibility, do not proceed
   // to starting ARC nor follow further state transitions.
@@ -758,7 +758,7 @@
   DCHECK(!terms_of_service_negotiator_);
   // In Kiosk and Public Session mode, Terms of Service negotiation should be
   // skipped. See also RequestEnableImpl().
-  DCHECK(!IsRobotAccountMode());
+  DCHECK(!IsRobotOrOfflineDemoAccountMode());
   // If opt-in verification is disabled, Terms of Service negotiation should
   // be skipped, too. See also RequestEnableImpl().
   DCHECK(!IsArcOptInVerificationDisabled());
@@ -905,7 +905,7 @@
   // Skip Android management check for testing.
   // We also skip if Android management check for Kiosk and Public Session mode,
   // because there are no managed human users for them exist.
-  if (IsArcOptInVerificationDisabled() || IsRobotAccountMode() ||
+  if (IsArcOptInVerificationDisabled() || IsRobotOrOfflineDemoAccountMode() ||
       (!g_ui_enabled &&
        !g_enable_check_android_management_in_tests.value_or(false))) {
     return;
diff --git a/chrome/browser/chromeos/arc/auth/arc_auth_service.cc b/chrome/browser/chromeos/arc/auth/arc_auth_service.cc
index 0cb614fd..fefafe76 100644
--- a/chrome/browser/chromeos/arc/auth/arc_auth_service.cc
+++ b/chrome/browser/chromeos/arc/auth/arc_auth_service.cc
@@ -15,6 +15,7 @@
 #include "chrome/browser/chromeos/arc/auth/arc_background_auth_code_fetcher.h"
 #include "chrome/browser/chromeos/arc/auth/arc_robot_auth_code_fetcher.h"
 #include "chrome/browser/chromeos/arc/policy/arc_policy_util.h"
+#include "chrome/browser/chromeos/login/demo_mode/demo_session.h"
 #include "chrome/browser/lifetime/application_lifetime.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chromeos/chromeos_switches.h"
@@ -97,8 +98,22 @@
   if (profile->IsChild())
     return mojom::ChromeAccountType::CHILD_ACCOUNT;
 
-  return IsRobotAccountMode() ? mojom::ChromeAccountType::ROBOT_ACCOUNT
-                              : mojom::ChromeAccountType::USER_ACCOUNT;
+  chromeos::DemoSession* demo_session = chromeos::DemoSession::Get();
+  if (demo_session && demo_session->started()) {
+    // Internally, demo mode is implemented as a public session, and should
+    // generally follow normal robot account provisioning flow. Offline enrolled
+    // demo mode is an exception, as it is expected to work purely offline, with
+    // a (fake) robot account not known to auth service - this means that it has
+    // to go through different, offline provisioning flow.
+    DCHECK(IsRobotOrOfflineDemoAccountMode());
+    return demo_session->offline_enrolled()
+               ? mojom::ChromeAccountType::OFFLINE_DEMO_ACCOUNT
+               : mojom::ChromeAccountType::ROBOT_ACCOUNT;
+  }
+
+  return IsRobotOrOfflineDemoAccountMode()
+             ? mojom::ChromeAccountType::ROBOT_ACCOUNT
+             : mojom::ChromeAccountType::USER_ACCOUNT;
 }
 
 mojom::AccountInfoPtr CreateAccountInfo(bool is_enforced,
@@ -270,7 +285,7 @@
   }
   // For non-AD enrolled devices an auth code is fetched.
   std::unique_ptr<ArcAuthCodeFetcher> auth_code_fetcher;
-  if (IsRobotAccountMode()) {
+  if (IsRobotOrOfflineDemoAccountMode()) {
     // In Kiosk and public session mode, use Robot auth code fetching.
     auth_code_fetcher = std::make_unique<ArcRobotAuthCodeFetcher>();
   } else {
diff --git a/chrome/browser/chromeos/arc/auth/arc_auth_service_browsertest.cc b/chrome/browser/chromeos/arc/auth/arc_auth_service_browsertest.cc
index c6ee393..a62ff6a 100644
--- a/chrome/browser/chromeos/arc/auth/arc_auth_service_browsertest.cc
+++ b/chrome/browser/chromeos/arc/auth/arc_auth_service_browsertest.cc
@@ -13,14 +13,18 @@
 #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/chromeos/arc/arc_service_launcher.h"
 #include "chrome/browser/chromeos/arc/arc_session_manager.h"
 #include "chrome/browser/chromeos/arc/arc_util.h"
 #include "chrome/browser/chromeos/arc/auth/arc_auth_context.h"
 #include "chrome/browser/chromeos/arc/auth/arc_auth_service.h"
 #include "chrome/browser/chromeos/arc/auth/arc_background_auth_code_fetcher.h"
+#include "chrome/browser/chromeos/login/demo_mode/demo_session.h"
 #include "chrome/browser/chromeos/login/users/fake_chrome_user_manager.h"
+#include "chrome/browser/chromeos/policy/browser_policy_connector_chromeos.h"
 #include "chrome/browser/chromeos/profiles/profile_helper.h"
+#include "chrome/browser/policy/cloud/test_request_interceptor.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/signin/fake_profile_oauth2_token_service_builder.h"
 #include "chrome/browser/signin/fake_signin_manager_builder.h"
@@ -39,11 +43,16 @@
 #include "components/arc/arc_util.h"
 #include "components/arc/test/connection_holder_util.h"
 #include "components/arc/test/fake_arc_session.h"
+#include "components/policy/core/common/cloud/device_management_service.h"
+#include "components/policy/core/common/cloud/mock_cloud_policy_client.h"
+#include "components/policy/core/common/policy_switches.h"
 #include "components/prefs/pref_member.h"
 #include "components/prefs/pref_service.h"
 #include "components/signin/core/browser/fake_profile_oauth2_token_service.h"
 #include "components/user_manager/scoped_user_manager.h"
 #include "components/user_manager/user_manager.h"
+#include "content/public/browser/browser_thread.h"
+#include "net/url_request/url_request_test_job.h"
 #include "services/network/public/cpp/shared_url_loader_factory.h"
 #include "services/network/public/cpp/weak_wrapper_shared_url_loader_factory.h"
 #include "services/network/test/test_url_loader_factory.h"
@@ -146,9 +155,21 @@
   void SetAccountAndProfile(const user_manager::UserType user_type) {
     const AccountId account_id(
         AccountId::FromUserEmailGaiaId(kFakeUserName, kFakeGaiaId));
-    user_type == user_manager::USER_TYPE_CHILD
-        ? GetFakeUserManager()->AddChildUser(account_id)
-        : GetFakeUserManager()->AddUser(account_id);
+    switch (user_type) {
+      case user_manager::USER_TYPE_CHILD:
+        GetFakeUserManager()->AddChildUser(account_id);
+        break;
+      case user_manager::USER_TYPE_REGULAR:
+        GetFakeUserManager()->AddUser(account_id);
+        break;
+      case user_manager::USER_TYPE_PUBLIC_ACCOUNT:
+        GetFakeUserManager()->AddPublicAccountUser(account_id);
+        break;
+      default:
+        ADD_FAILURE() << "Unexpected user type " << user_type;
+        return;
+    }
+
     GetFakeUserManager()->LoginUser(account_id);
     GetFakeUserManager()->CreateLocalState();
 
@@ -242,6 +263,142 @@
   arc_bridge_service->auth()->CloseInstance(&auth_instance);
 }
 
+class ArcRobotAccountAuthServiceTest : public ArcAuthServiceTest {
+ public:
+  ArcRobotAccountAuthServiceTest() = default;
+  ~ArcRobotAccountAuthServiceTest() override = default;
+
+  void SetUpCommandLine(base::CommandLine* command_line) override {
+    command_line->AppendSwitchASCII(policy::switches::kDeviceManagementUrl,
+                                    "http://localhost");
+    ArcAuthServiceTest::SetUpCommandLine(command_line);
+  }
+
+  void SetUpOnMainThread() override {
+    ArcAuthServiceTest::SetUpOnMainThread();
+    interceptor_ = std::make_unique<policy::TestRequestInterceptor>(
+        "localhost", content::BrowserThread::GetTaskRunnerForThread(
+                         content::BrowserThread::IO));
+    SetUpPolicyClient();
+  }
+
+  void TearDownOnMainThread() override {
+    ArcAuthServiceTest::TearDownOnMainThread();
+
+    // Verify that all the expected requests were handled.
+    EXPECT_EQ(0u, interceptor_->GetPendingSize());
+    interceptor_.reset();
+  }
+
+ protected:
+  // JobCallback for the interceptor.
+  static net::URLRequestJob* ResponseJob(
+      net::URLRequest* request,
+      net::NetworkDelegate* network_delegate) {
+    enterprise_management::DeviceManagementResponse response;
+    response.mutable_service_api_access_response()->set_auth_code(
+        kFakeAuthCode);
+
+    std::string response_data;
+    EXPECT_TRUE(response.SerializeToString(&response_data));
+
+    return new net::URLRequestTestJob(request, network_delegate,
+                                      net::URLRequestTestJob::test_headers(),
+                                      response_data, true);
+  }
+
+  policy::TestRequestInterceptor* interceptor() { return interceptor_.get(); }
+
+ private:
+  void SetUpPolicyClient() {
+    policy::BrowserPolicyConnectorChromeOS* const connector =
+        g_browser_process->platform_part()->browser_policy_connector_chromeos();
+    policy::DeviceCloudPolicyManagerChromeOS* const cloud_policy_manager =
+        connector->GetDeviceCloudPolicyManager();
+
+    cloud_policy_manager->StartConnection(
+        std::make_unique<policy::MockCloudPolicyClient>(),
+        connector->GetInstallAttributes());
+
+    policy::MockCloudPolicyClient* const cloud_policy_client =
+        static_cast<policy::MockCloudPolicyClient*>(
+            cloud_policy_manager->core()->client());
+    cloud_policy_client->SetDMToken("fake-dm-token");
+    cloud_policy_client->client_id_ = "client-id";
+  }
+
+  std::unique_ptr<policy::TestRequestInterceptor> interceptor_;
+
+  DISALLOW_COPY_AND_ASSIGN(ArcRobotAccountAuthServiceTest);
+};
+
+// Tests that when ARC requests account info for a demo session account,
+// Chrome supplies the info configured in SetAccountAndProfile() above.
+IN_PROC_BROWSER_TEST_F(ArcRobotAccountAuthServiceTest, GetDemoAccount) {
+  chromeos::DemoSession::SetDemoModeEnrollmentTypeForTesting(
+      chromeos::DemoSession::EnrollmentType::kOnline);
+  chromeos::DemoSession::StartIfInDemoMode();
+
+  SetAccountAndProfile(user_manager::USER_TYPE_PUBLIC_ACCOUNT);
+
+  interceptor()->PushJobCallback(
+      base::Bind(&ArcRobotAccountAuthServiceTest::ResponseJob));
+
+  ArcBridgeService* arc_bridge_service =
+      ArcServiceManager::Get()->arc_bridge_service();
+  ASSERT_TRUE(arc_bridge_service);
+
+  FakeAuthInstance auth_instance;
+  arc_bridge_service->auth()->SetInstance(&auth_instance);
+  WaitForInstanceReady(arc_bridge_service->auth());
+
+  base::RunLoop run_loop;
+  auth_instance.RequestAccountInfo(run_loop.QuitClosure());
+  run_loop.Run();
+
+  ASSERT_TRUE(auth_instance.account_info());
+  EXPECT_EQ(kFakeUserName, auth_instance.account_info()->account_name.value());
+  EXPECT_EQ(kFakeAuthCode, auth_instance.account_info()->auth_code.value());
+  EXPECT_EQ(mojom::ChromeAccountType::ROBOT_ACCOUNT,
+            auth_instance.account_info()->account_type);
+  EXPECT_FALSE(auth_instance.account_info()->enrollment_token);
+  EXPECT_FALSE(auth_instance.account_info()->is_managed);
+
+  arc_bridge_service->auth()->CloseInstance(&auth_instance);
+}
+
+IN_PROC_BROWSER_TEST_F(ArcRobotAccountAuthServiceTest, GetOfflineDemoAccount) {
+  chromeos::DemoSession::SetDemoModeEnrollmentTypeForTesting(
+      chromeos::DemoSession::EnrollmentType::kOffline);
+  chromeos::DemoSession::StartIfInDemoMode();
+
+  SetAccountAndProfile(user_manager::USER_TYPE_PUBLIC_ACCOUNT);
+  interceptor()->PushJobCallback(
+      base::Bind(&ArcRobotAccountAuthServiceTest::ResponseJob));
+
+  ArcBridgeService* arc_bridge_service =
+      ArcServiceManager::Get()->arc_bridge_service();
+  ASSERT_TRUE(arc_bridge_service);
+
+  FakeAuthInstance auth_instance;
+  arc_bridge_service->auth()->SetInstance(&auth_instance);
+  WaitForInstanceReady(arc_bridge_service->auth());
+
+  base::RunLoop run_loop;
+  auth_instance.RequestAccountInfo(run_loop.QuitClosure());
+  run_loop.Run();
+
+  ASSERT_TRUE(auth_instance.account_info());
+  EXPECT_EQ(kFakeUserName, auth_instance.account_info()->account_name.value());
+  EXPECT_EQ(kFakeAuthCode, auth_instance.account_info()->auth_code.value());
+  EXPECT_EQ(mojom::ChromeAccountType::OFFLINE_DEMO_ACCOUNT,
+            auth_instance.account_info()->account_type);
+  EXPECT_FALSE(auth_instance.account_info()->enrollment_token);
+  EXPECT_FALSE(auth_instance.account_info()->is_managed);
+
+  arc_bridge_service->auth()->CloseInstance(&auth_instance);
+}
+
 class ArcAuthServiceChildAccountTest : public ArcAuthServiceTest {
  protected:
   ArcAuthServiceChildAccountTest() = default;
diff --git a/chrome/browser/chromeos/login/app_launch_controller.cc b/chrome/browser/chromeos/login/app_launch_controller.cc
index ee49c21..1c9f2ce 100644
--- a/chrome/browser/chromeos/login/app_launch_controller.cc
+++ b/chrome/browser/chromeos/login/app_launch_controller.cc
@@ -4,6 +4,7 @@
 
 #include "chrome/browser/chromeos/login/app_launch_controller.h"
 
+#include "ash/public/cpp/ash_features.h"
 #include "ash/shell.h"
 #include "base/bind.h"
 #include "base/callback.h"
@@ -166,18 +167,18 @@
   RecordKioskLaunchUMA(is_auto_launch);
 
   // Ensure WebUILoginView is enabled so that bailout shortcut key works.
-  WebUILoginView* webui = host_->GetWebUILoginView();
-  if (webui) {
-    webui->SetUIEnabled(true);
-
-    login_screen_visible_ = webui->webui_visible();
+  if (ash::features::IsViewsLoginEnabled()) {
+    host_->GetLoginDisplay()->SetUIEnabled(true);
+    login_screen_visible_ = true;
+  } else {
+    host_->GetWebUILoginView()->SetUIEnabled(true);
+    login_screen_visible_ = host_->GetWebUILoginView()->webui_visible();
     if (!login_screen_visible_) {
       registrar_.Add(this, chrome::NOTIFICATION_LOGIN_OR_LOCK_WEBUI_VISIBLE,
                      content::NotificationService::AllSources());
     }
-  } else {
-    login_screen_visible_ = true;
   }
+
   launch_splash_start_time_ = base::TimeTicks::Now().ToInternalValue();
 
   // TODO(tengs): Add a loading profile app launch state.
diff --git a/chrome/browser/chromeos/login/arc_kiosk_controller.cc b/chrome/browser/chromeos/login/arc_kiosk_controller.cc
index 0c74db4..4cc6265 100644
--- a/chrome/browser/chromeos/login/arc_kiosk_controller.cc
+++ b/chrome/browser/chromeos/login/arc_kiosk_controller.cc
@@ -41,7 +41,7 @@
 void ArcKioskController::StartArcKiosk(const AccountId& account_id) {
   DVLOG(1) << "Starting ARC Kiosk for account: " << account_id.GetUserEmail();
 
-  host_->GetWebUILoginView()->SetUIEnabled(true);
+  host_->GetLoginDisplay()->SetUIEnabled(true);
 
   arc_kiosk_splash_screen_view_->SetDelegate(this);
   arc_kiosk_splash_screen_view_->Show();
diff --git a/chrome/browser/chromeos/login/demo_mode/demo_session.cc b/chrome/browser/chromeos/login/demo_mode/demo_session.cc
index 65dd422..5bea8ee 100644
--- a/chrome/browser/chromeos/login/demo_mode/demo_session.cc
+++ b/chrome/browser/chromeos/login/demo_mode/demo_session.cc
@@ -10,6 +10,7 @@
 #include "base/callback.h"
 #include "chromeos/dbus/dbus_thread_manager.h"
 #include "chromeos/dbus/image_loader_client.h"
+#include "content/public/browser/browser_thread.h"
 
 namespace chromeos {
 
@@ -19,7 +20,8 @@
 DemoSession* g_demo_session = nullptr;
 
 // Whether the demo mode was forced on for tests.
-bool g_force_device_in_demo_mode = false;
+DemoSession::EnrollmentType g_force_enrollment_type =
+    DemoSession::EnrollmentType::kNone;
 
 // The name of the offline demo resource image loader component.
 constexpr char kOfflineResourcesComponentName[] = "demo_mode_resources";
@@ -35,17 +37,27 @@
 constexpr base::FilePath::CharType kDemoAppsPath[] =
     FILE_PATH_LITERAL("android_demo_apps.squash");
 
+bool IsDemoModeOfflineEnrolled() {
+  DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
+  DCHECK(DemoSession::IsDeviceInDemoMode());
+  // TODO(tbarzic): Implement this.
+  return g_force_enrollment_type == DemoSession::EnrollmentType::kOffline;
+}
+
 }  // namespace
 
 // static
 bool DemoSession::IsDeviceInDemoMode() {
+  DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
   // TODO(tbarzic): Implement this.
-  return g_force_device_in_demo_mode;
+  return g_force_enrollment_type != DemoSession::EnrollmentType::kNone;
 }
 
 // static
-void DemoSession::SetDeviceInDemoModeForTesting(bool in_demo_mode) {
-  g_force_device_in_demo_mode = in_demo_mode;
+void DemoSession::SetDemoModeEnrollmentTypeForTesting(
+    EnrollmentType enrollment_type) {
+  DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
+  g_force_enrollment_type = enrollment_type;
 }
 
 // static
@@ -119,7 +131,8 @@
   return offline_resources_path_.Append(kDemoAppsPath);
 }
 
-DemoSession::DemoSession() : weak_ptr_factory_(this) {}
+DemoSession::DemoSession()
+    : offline_enrolled_(IsDemoModeOfflineEnrolled()), weak_ptr_factory_(this) {}
 
 DemoSession::~DemoSession() = default;
 
diff --git a/chrome/browser/chromeos/login/demo_mode/demo_session.h b/chrome/browser/chromeos/login/demo_mode/demo_session.h
index 1697730..4b05bb6 100644
--- a/chrome/browser/chromeos/login/demo_mode/demo_session.h
+++ b/chrome/browser/chromeos/login/demo_mode/demo_session.h
@@ -19,9 +19,23 @@
 // started, and whether the demo session offline resources have been loaded.
 class DemoSession {
  public:
+  enum class EnrollmentType {
+    // Device was not enrolled into demo mode
+    kNone,
+
+    // Device was enrolled into demo mode using online enrollment flow.
+    kOnline,
+
+    // Device was enrolled into demo mode using offline enrollment flow - i.e.
+    // policies are retrieved from the device, rather than from DM server.
+    kOffline,
+  };
+
   // Whether the device is set up to run demo sessions.
   static bool IsDeviceInDemoMode();
-  static void SetDeviceInDemoModeForTesting(bool in_demo_mode);
+
+  static void SetDemoModeEnrollmentTypeForTesting(
+      EnrollmentType enrollment_type);
 
   // If the device is set up to run in demo mode, marks demo session as started,
   // and requests load of demo session resources.
@@ -48,7 +62,10 @@
   // will be set when the offline resources get loaded.
   base::FilePath GetDemoAppsPath() const;
 
+  bool offline_enrolled() const { return offline_enrolled_; }
+
   bool started() const { return started_; }
+
   bool offline_resources_loaded() const { return offline_resources_loaded_; }
 
  private:
@@ -59,6 +76,12 @@
   // |mount_path| is the path at which the resources were loaded.
   void OnOfflineResourcesLoaded(base::Optional<base::FilePath> mounted_path);
 
+  // Whether the device was offline-enrolled into demo mode, i.e. enrolled using
+  // pre-built policies. Offline enrolled demo sessions do not have working
+  // robot account associated with them.
+  bool offline_enrolled_ = false;
+
+  // Whether demo session has been started.
   bool started_ = false;
 
   bool offline_resources_load_requested_ = false;
diff --git a/chrome/browser/chromeos/login/demo_mode/demo_session_unittest.cc b/chrome/browser/chromeos/login/demo_mode/demo_session_unittest.cc
index a25e4db..ed44fc9 100644
--- a/chrome/browser/chromeos/login/demo_mode/demo_session_unittest.cc
+++ b/chrome/browser/chromeos/login/demo_mode/demo_session_unittest.cc
@@ -15,6 +15,7 @@
 #include "base/macros.h"
 #include "chromeos/dbus/dbus_thread_manager.h"
 #include "chromeos/dbus/fake_image_loader_client.h"
+#include "content/public/test/test_browser_thread_bundle.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
 namespace chromeos {
@@ -112,7 +113,8 @@
   ~DemoSessionTest() override = default;
 
   void SetUp() override {
-    chromeos::DemoSession::SetDeviceInDemoModeForTesting(true);
+    DemoSession::SetDemoModeEnrollmentTypeForTesting(
+        DemoSession::EnrollmentType::kOnline);
     auto image_loader_client = std::make_unique<TestImageLoaderClient>();
     image_loader_client_ = image_loader_client.get();
     chromeos::DBusThreadManager::GetSetterForTesting()->SetImageLoaderClient(
@@ -121,6 +123,8 @@
 
   void TearDown() override {
     DemoSession::ShutDownIfInitialized();
+    DemoSession::SetDemoModeEnrollmentTypeForTesting(
+        DemoSession::EnrollmentType::kNone);
     image_loader_client_ = nullptr;
     chromeos::DBusThreadManager::Shutdown();
   }
@@ -128,6 +132,7 @@
  protected:
   // Points to the image loader client passed to the test DBusTestManager.
   TestImageLoaderClient* image_loader_client_ = nullptr;
+  content::TestBrowserThreadBundle thread_bundle_;
 
  private:
   DISALLOW_COPY_AND_ASSIGN(DemoSessionTest);
@@ -138,6 +143,7 @@
   DemoSession* demo_session = DemoSession::StartIfInDemoMode();
   ASSERT_TRUE(demo_session);
   EXPECT_TRUE(demo_session->started());
+  EXPECT_FALSE(demo_session->offline_enrolled());
   EXPECT_EQ(demo_session, DemoSession::Get());
 }
 
@@ -160,7 +166,8 @@
 }
 
 TEST_F(DemoSessionTest, StartForDemoDeviceNotInDemoMode) {
-  DemoSession::SetDeviceInDemoModeForTesting(false);
+  DemoSession::SetDemoModeEnrollmentTypeForTesting(
+      DemoSession::EnrollmentType::kNone);
   EXPECT_FALSE(DemoSession::Get());
   EXPECT_FALSE(DemoSession::StartIfInDemoMode());
   EXPECT_FALSE(DemoSession::Get());
@@ -168,12 +175,29 @@
   EXPECT_EQ(std::list<std::string>(), image_loader_client_->pending_loads());
 }
 
+TEST_F(DemoSessionTest, StartIfInOfflineEnrolledDemoMode) {
+  DemoSession::SetDemoModeEnrollmentTypeForTesting(
+      DemoSession::EnrollmentType::kOffline);
+
+  EXPECT_FALSE(DemoSession::Get());
+  DemoSession* demo_session = DemoSession::StartIfInDemoMode();
+  ASSERT_TRUE(demo_session);
+  EXPECT_TRUE(demo_session->started());
+  EXPECT_TRUE(demo_session->offline_enrolled());
+  EXPECT_EQ(demo_session, DemoSession::Get());
+
+  EXPECT_FALSE(demo_session->offline_resources_loaded());
+  EXPECT_EQ(std::list<std::string>({kOfflineResourcesComponent}),
+            image_loader_client_->pending_loads());
+}
+
 TEST_F(DemoSessionTest, PreloadOfflineResourcesIfInDemoMode) {
   DemoSession::PreloadOfflineResourcesIfInDemoMode();
 
   DemoSession* demo_session = DemoSession::Get();
   ASSERT_TRUE(demo_session);
   EXPECT_FALSE(demo_session->started());
+  EXPECT_FALSE(demo_session->offline_enrolled());
 
   EXPECT_FALSE(demo_session->offline_resources_loaded());
   EXPECT_EQ(std::list<std::string>({kOfflineResourcesComponent}),
@@ -191,12 +215,28 @@
 }
 
 TEST_F(DemoSessionTest, PreloadOfflineResourcesIfNotInDemoMode) {
-  DemoSession::SetDeviceInDemoModeForTesting(false);
+  DemoSession::SetDemoModeEnrollmentTypeForTesting(
+      DemoSession::EnrollmentType::kNone);
   DemoSession::PreloadOfflineResourcesIfInDemoMode();
   EXPECT_FALSE(DemoSession::Get());
   EXPECT_EQ(std::list<std::string>(), image_loader_client_->pending_loads());
 }
 
+TEST_F(DemoSessionTest, PreloadOfflineResourcesIfInOfflineDemoMode) {
+  DemoSession::SetDemoModeEnrollmentTypeForTesting(
+      DemoSession::EnrollmentType::kOffline);
+  DemoSession::PreloadOfflineResourcesIfInDemoMode();
+
+  DemoSession* demo_session = DemoSession::Get();
+  ASSERT_TRUE(demo_session);
+  EXPECT_FALSE(demo_session->started());
+  EXPECT_TRUE(demo_session->offline_enrolled());
+
+  EXPECT_FALSE(demo_session->offline_resources_loaded());
+  EXPECT_EQ(std::list<std::string>({kOfflineResourcesComponent}),
+            image_loader_client_->pending_loads());
+}
+
 TEST_F(DemoSessionTest, ShutdownResetsInstance) {
   ASSERT_TRUE(DemoSession::StartIfInDemoMode());
   EXPECT_TRUE(DemoSession::Get());
diff --git a/chrome/browser/chromeos/login/easy_unlock/easy_unlock_service_regular.cc b/chrome/browser/chromeos/login/easy_unlock/easy_unlock_service_regular.cc
index 0fd297f..aa80af1a 100644
--- a/chrome/browser/chromeos/login/easy_unlock/easy_unlock_service_regular.cc
+++ b/chrome/browser/chromeos/login/easy_unlock/easy_unlock_service_regular.cc
@@ -130,16 +130,10 @@
       shown_pairing_changed_notification_(false),
       weak_ptr_factory_(this) {
   if (base::FeatureList::IsEnabled(chromeos::features::kMultiDeviceApi)) {
-    // If this is the first GetSyncedDevices() call on DeviceSyncClient, it
-    // won't have devices cached yet. |is_waiting_for_initial_sync_| is flipped
-    // to indicate that we are waiting for this initial sync, to be inspected
-    // in OnNewDevicesSynced(). OnNewDevicesSynced() needs to know that it is
-    // receiving the initial sync, not a newly forced one, in order to prevent
-    // it from running unrelated logic.
-    if (device_sync_client_->GetSyncedDevices().empty())
-      is_waiting_for_initial_sync_ = true;
-    else
-      remote_device_unlock_keys_before_sync_ = GetUnlockKeys();
+    // If |device_sync_client_| is not ready yet, wait for it to call back on
+    // OnReady().
+    if (device_sync_client_->is_ready())
+      OnReady();
 
     device_sync_client_->AddObserver(this);
   }
@@ -157,11 +151,11 @@
 // explicit.
 void EasyUnlockServiceRegular::LoadRemoteDevices() {
   if (base::FeatureList::IsEnabled(chromeos::features::kMultiDeviceApi) &&
-      !device_sync_client_->GetLocalDeviceMetadata()) {
-    // OnEnrollmentFinished() will call back on this method once the local
-    // device is ready.
-    PA_LOG(INFO)
-        << "Local device is not ready yet, delaying UseLoadedRemoteDevices().";
+      !device_sync_client_->is_ready()) {
+    // OnEnrollmentFinished() or OnNewDevicesSynced() will call back on this
+    // method once |device_sync_client_| is ready.
+    PA_LOG(INFO) << "DeviceSyncClient is not ready yet, delaying "
+                    "UseLoadedRemoteDevices().";
     return;
   }
 
@@ -622,9 +616,16 @@
   LoadRemoteDevices();
 }
 
+void EasyUnlockServiceRegular::OnReady() {
+  // If the local device and synced devices are ready for the first time,
+  // establish what the unlock keys were before the next sync. This is necessary
+  // in order for OnNewDevicesSynced() to determine if new devices were added
+  // since the last sync.
+  remote_device_unlock_keys_before_sync_ = GetUnlockKeys();
+}
+
 void EasyUnlockServiceRegular::OnEnrollmentFinished() {
-  // If the local device is ready for the first time, or has been updated,
-  // reload devices.
+  // If the local device has been updated, reload devices.
   LoadRemoteDevices();
 }
 
@@ -635,12 +636,6 @@
   DCHECK(base::FeatureList::IsEnabled(chromeos::features::kMultiDeviceApi));
 
   std::set<std::string> public_keys_before_sync;
-  if (is_waiting_for_initial_sync_) {
-    is_waiting_for_initial_sync_ = false;
-
-    // Without this, |public_keys_before_sync| would be incorrectly empty.
-    remote_device_unlock_keys_before_sync_ = GetUnlockKeys();
-  }
   for (const auto& remote_device : remote_device_unlock_keys_before_sync_) {
     public_keys_before_sync.insert(remote_device.public_key());
   }
diff --git a/chrome/browser/chromeos/login/easy_unlock/easy_unlock_service_regular.h b/chrome/browser/chromeos/login/easy_unlock/easy_unlock_service_regular.h
index ec0a507a..24675ea 100644
--- a/chrome/browser/chromeos/login/easy_unlock/easy_unlock_service_regular.h
+++ b/chrome/browser/chromeos/login/easy_unlock/easy_unlock_service_regular.h
@@ -115,6 +115,7 @@
                           device_change_result) override;
 
   // device_sync::DeviceSyncClient::Observer:
+  void OnReady() override;
   void OnEnrollmentFinished() override;
   void OnNewDevicesSynced() override;
 
@@ -220,14 +221,6 @@
   // notification.
   bool shown_pairing_changed_notification_;
 
-  // If this service is the first caller on DeviceSyncClient, it won't have
-  // devices cached yet. |is_waiting_for_initial_sync_| is set to true if
-  // DeviceSyncClient has no devices, to indicate that we are waiting for the
-  // initial sync, to be inspected in OnNewDevicesSynced(). OnNewDevicesSynced()
-  // needs to know that it is receiving the initial sync, not a newly forced
-  // one, in order to prevent it from running unrelated logic.
-  bool is_waiting_for_initial_sync_ = false;
-
   // Listens to pref changes.
   PrefChangeRegistrar registrar_;
 
diff --git a/chrome/browser/chromeos/login/ui/kiosk_app_menu_updater.cc b/chrome/browser/chromeos/login/ui/kiosk_app_menu_updater.cc
index 5bcaa1b..d786839 100644
--- a/chrome/browser/chromeos/login/ui/kiosk_app_menu_updater.cc
+++ b/chrome/browser/chromeos/login/ui/kiosk_app_menu_updater.cc
@@ -6,6 +6,8 @@
 
 #include "ash/public/interfaces/kiosk_app_info.mojom.h"
 #include "base/strings/utf_string_conversions.h"
+#include "chrome/browser/chromeos/app_mode/arc/arc_kiosk_app_data.h"
+#include "chrome/browser/chromeos/app_mode/arc/arc_kiosk_app_manager.h"
 #include "chrome/browser/chromeos/app_mode/kiosk_app_manager.h"
 #include "chrome/browser/chromeos/app_mode/kiosk_app_manager_observer.h"
 #include "chrome/browser/ui/ash/login_screen_client.h"
@@ -15,13 +17,13 @@
 
 namespace chromeos {
 
-KioskAppMenuUpdater::KioskAppMenuUpdater() {
-  KioskAppManager::Get()->AddObserver(this);
+KioskAppMenuUpdater::KioskAppMenuUpdater()
+    : kiosk_observer_(this), arc_kiosk_observer_(this) {
+  kiosk_observer_.Add(KioskAppManager::Get());
+  arc_kiosk_observer_.Add(ArcKioskAppManager::Get());
 }
 
-KioskAppMenuUpdater::~KioskAppMenuUpdater() {
-  KioskAppManager::Get()->RemoveObserver(this);
-}
+KioskAppMenuUpdater::~KioskAppMenuUpdater() = default;
 
 void KioskAppMenuUpdater::OnKioskAppDataChanged(const std::string& app_id) {
   SendKioskApps();
@@ -35,17 +37,22 @@
   SendKioskApps();
 }
 
+void KioskAppMenuUpdater::OnArcKioskAppsChanged() {
+  SendKioskApps();
+}
+
 void KioskAppMenuUpdater::SendKioskApps() {
   if (!LoginScreenClient::HasInstance())
     return;
 
   std::vector<ash::mojom::KioskAppInfoPtr> output;
-  std::vector<chromeos::KioskAppManager::App> apps;
 
-  chromeos::KioskAppManager::Get()->GetApps(&apps);
+  std::vector<KioskAppManager::App> apps;
+  KioskAppManager::Get()->GetApps(&apps);
   for (const auto& app : apps) {
     auto mojo_app = ash::mojom::KioskAppInfo::New();
-    mojo_app->app_id = app.app_id;
+    mojo_app->identifier = ash::mojom::KioskAppIdentifier::New();
+    mojo_app->identifier->set_app_id(app.app_id);
     mojo_app->name = base::UTF8ToUTF16(app.name);
     if (app.icon.isNull()) {
       mojo_app->icon = *ui::ResourceBundle::GetSharedInstance()
@@ -57,6 +64,23 @@
     output.push_back(std::move(mojo_app));
   }
 
+  std::vector<ArcKioskAppData*> arc_apps;
+  ArcKioskAppManager::Get()->GetAllApps(&arc_apps);
+  for (ArcKioskAppData* app : arc_apps) {
+    auto mojo_app = ash::mojom::KioskAppInfo::New();
+    mojo_app->identifier = ash::mojom::KioskAppIdentifier::New();
+    mojo_app->identifier->set_account_id(app->account_id());
+    mojo_app->name = base::UTF8ToUTF16(app->name());
+    if (app->icon().isNull()) {
+      mojo_app->icon = *ui::ResourceBundle::GetSharedInstance()
+                            .GetImageNamed(IDR_APP_DEFAULT_ICON)
+                            .ToImageSkia();
+    } else {
+      mojo_app->icon = gfx::ImageSkia(app->icon());
+    }
+    output.push_back(std::move(mojo_app));
+  }
+
   LoginScreenClient::Get()->login_screen()->SetKioskApps(std::move(output));
 }
 
diff --git a/chrome/browser/chromeos/login/ui/kiosk_app_menu_updater.h b/chrome/browser/chromeos/login/ui/kiosk_app_menu_updater.h
index 799d4532..96e092c 100644
--- a/chrome/browser/chromeos/login/ui/kiosk_app_menu_updater.h
+++ b/chrome/browser/chromeos/login/ui/kiosk_app_menu_updater.h
@@ -6,12 +6,17 @@
 #define CHROME_BROWSER_CHROMEOS_LOGIN_UI_KIOSK_APP_MENU_UPDATER_H_
 
 #include "base/macros.h"
+#include "base/scoped_observer.h"
+#include "chrome/browser/chromeos/app_mode/arc/arc_kiosk_app_manager.h"
+#include "chrome/browser/chromeos/app_mode/kiosk_app_manager.h"
 #include "chrome/browser/chromeos/app_mode/kiosk_app_manager_observer.h"
 
 namespace chromeos {
 
 // Observer class to update the Kiosk app menu when Kiosk app data is changed.
-class KioskAppMenuUpdater : public KioskAppManagerObserver {
+class KioskAppMenuUpdater
+    : public KioskAppManagerObserver,
+      public ArcKioskAppManager::ArcKioskAppManagerObserver {
  public:
   KioskAppMenuUpdater();
   ~KioskAppMenuUpdater() override;
@@ -24,7 +29,13 @@
   void OnKioskAppDataLoadFailure(const std::string& app_id) override;
   void OnKioskAppsSettingsChanged() override;
 
+  // ArcKioskAppManagerObserver:
+  void OnArcKioskAppsChanged() override;
+
  private:
+  ScopedObserver<KioskAppManager, KioskAppMenuUpdater> kiosk_observer_;
+  ScopedObserver<ArcKioskAppManager, KioskAppMenuUpdater> arc_kiosk_observer_;
+
   DISALLOW_COPY_AND_ASSIGN(KioskAppMenuUpdater);
 };
 
diff --git a/chrome/browser/chromeos/login/ui/login_display_host_mojo.cc b/chrome/browser/chromeos/login/ui/login_display_host_mojo.cc
index c7933b3..4932436 100644
--- a/chrome/browser/chromeos/login/ui/login_display_host_mojo.cc
+++ b/chrome/browser/chromeos/login/ui/login_display_host_mojo.cc
@@ -211,7 +211,7 @@
 }
 
 void LoginDisplayHostMojo::OnStartArcKiosk() {
-  NOTIMPLEMENTED();
+  dialog_->ShowFullScreen();
 }
 
 void LoginDisplayHostMojo::OnBrowserCreated() {
diff --git a/chrome/browser/chromeos/login/ui/oobe_ui_dialog_delegate.cc b/chrome/browser/chromeos/login/ui/oobe_ui_dialog_delegate.cc
index 976a9b4e..86b2f440 100644
--- a/chrome/browser/chromeos/login/ui/oobe_ui_dialog_delegate.cc
+++ b/chrome/browser/chromeos/login/ui/oobe_ui_dialog_delegate.cc
@@ -81,9 +81,12 @@
 
 void OobeUIDialogDelegate::ShowFullScreen() {
   const gfx::Size& size =
-      display::Screen::GetScreen()->GetPrimaryDisplay().size();
+      display::Screen::GetScreen()
+          ->GetDisplayNearestWindow(dialog_widget_->GetNativeWindow())
+          .size();
   UpdateSizeAndPosition(size.width(), size.height());
   Show(false /*closable_by_esc*/);
+  showing_fullscreen_ = true;
 }
 
 void OobeUIDialogDelegate::Hide() {
@@ -103,15 +106,23 @@
   if (!dialog_widget_)
     return;
 
-  const gfx::Rect rect =
+  // display_manager.js sends the width and height of the content in
+  // updateScreenSize() when we show the app launch splash. Without this if
+  // statement, the dialog would be resized to just fit the content rather than
+  // show fullscreen as expected.
+  if (showing_fullscreen_)
+    return;
+
+  gfx::Rect display_rect =
       display::Screen::GetScreen()
           ->GetDisplayNearestWindow(dialog_widget_->GetNativeWindow())
-          .work_area();
+          .bounds();
 
   // Place the dialog in the center of the screen.
-  const gfx::Rect bounds(rect.x() + (rect.width() - size_.width()) / 2,
-                         rect.y() + (rect.height() - size_.height()) / 2,
-                         size_.width(), size_.height());
+  const gfx::Rect bounds(
+      display_rect.x() + (display_rect.width() - size_.width()) / 2,
+      display_rect.y() + (display_rect.height() - size_.height()) / 2,
+      size_.width(), size_.height());
   dialog_widget_->SetBounds(bounds);
 }
 
diff --git a/chrome/browser/chromeos/login/ui/oobe_ui_dialog_delegate.h b/chrome/browser/chromeos/login/ui/oobe_ui_dialog_delegate.h
index 0f74010..db5995b 100644
--- a/chrome/browser/chromeos/login/ui/oobe_ui_dialog_delegate.h
+++ b/chrome/browser/chromeos/login/ui/oobe_ui_dialog_delegate.h
@@ -109,6 +109,7 @@
   views::WebDialogView* dialog_view_ = nullptr;
   gfx::Size size_;
   bool closable_by_esc_ = true;
+  bool showing_fullscreen_ = false;
 
   ScopedObserver<display::Screen, display::DisplayObserver> display_observer_;
   ScopedObserver<TabletModeClient, TabletModeClientObserver>
diff --git a/chrome/browser/chromeos/tether/tether_service.cc b/chrome/browser/chromeos/tether/tether_service.cc
index 4f1c49e8..6a7a4e65 100644
--- a/chrome/browser/chromeos/tether/tether_service.cc
+++ b/chrome/browser/chromeos/tether/tether_service.cc
@@ -26,7 +26,6 @@
 #include "chromeos/network/device_state.h"
 #include "chromeos/network/network_connect.h"
 #include "chromeos/network/network_type_pattern.h"
-#include "chromeos/services/device_sync/public/cpp/device_sync_client.h"
 #include "chromeos/services/secure_channel/public/cpp/client/secure_channel_client.h"
 #include "components/cryptauth/cryptauth_enrollment_manager.h"
 #include "components/cryptauth/cryptauth_service.h"
@@ -163,6 +162,8 @@
   tether_host_fetcher_->AddObserver(this);
   power_manager_client_->AddObserver(this);
   network_state_handler_->AddObserver(this, FROM_HERE);
+  if (base::FeatureList::IsEnabled(chromeos::features::kMultiDeviceApi))
+    device_sync_client_->AddObserver(this);
 
   registrar_.Init(profile_->GetPrefs());
   registrar_.Add(prefs::kInstantTetheringAllowed,
@@ -174,6 +175,12 @@
   PA_LOG(INFO) << "TetherService has started. Initial user preference value: "
                << IsEnabledbyPreference();
 
+  if (base::FeatureList::IsEnabled(chromeos::features::kMultiDeviceApi) &&
+      !device_sync_client_->is_ready()) {
+    // Wait for OnReady() to be called. It will call GetAdapter().
+    return;
+  }
+
   // GetAdapter may call OnBluetoothAdapterFetched immediately which can cause
   // problems with the Fake implementation since the class is not fully
   // constructed yet. Post the GetAdapter call to avoid this.
@@ -276,6 +283,8 @@
   tether_host_fetcher_->RemoveObserver(this);
   power_manager_client_->RemoveObserver(this);
   network_state_handler_->RemoveObserver(this, FROM_HERE);
+  if (base::FeatureList::IsEnabled(chromeos::features::kMultiDeviceApi))
+    device_sync_client_->RemoveObserver(this);
   if (adapter_)
     adapter_->RemoveObserver(this);
   registrar_.RemoveAll();
@@ -389,6 +398,15 @@
     StartTetherIfPossible();
 }
 
+void TetherService::OnReady() {
+  if (shut_down_)
+    return;
+
+  device::BluetoothAdapterFactory::GetAdapter(
+      base::Bind(&TetherService::OnBluetoothAdapterFetched,
+                 weak_ptr_factory_.GetWeakPtr()));
+}
+
 void TetherService::OnPrefsChanged() {
   UpdateTetherTechnologyState();
 }
diff --git a/chrome/browser/chromeos/tether/tether_service.h b/chrome/browser/chromeos/tether/tether_service.h
index cafbcd9..112d5c76 100644
--- a/chrome/browser/chromeos/tether/tether_service.h
+++ b/chrome/browser/chromeos/tether/tether_service.h
@@ -16,6 +16,7 @@
 #include "chromeos/dbus/power_manager_client.h"
 #include "chromeos/network/network_state_handler.h"
 #include "chromeos/network/network_state_handler_observer.h"
+#include "chromeos/services/device_sync/public/cpp/device_sync_client.h"
 #include "components/cryptauth/cryptauth_device_manager.h"
 #include "components/keyed_service/core/keyed_service.h"
 #include "components/prefs/pref_change_registrar.h"
@@ -25,9 +26,6 @@
 
 namespace chromeos {
 class NetworkStateHandler;
-namespace device_sync {
-class DeviceSyncClient;
-}  // namespace device_sync
 namespace secure_channel {
 class SecureChannelClient;
 }  // namespace secure_channel
@@ -62,7 +60,8 @@
                       public chromeos::tether::TetherHostFetcher::Observer,
                       public device::BluetoothAdapter::Observer,
                       public chromeos::NetworkStateHandlerObserver,
-                      public chromeos::tether::TetherComponent::Observer {
+                      public chromeos::tether::TetherComponent::Observer,
+                      public chromeos::device_sync::DeviceSyncClient::Observer {
  public:
   TetherService(
       Profile* profile,
@@ -116,6 +115,9 @@
   // chromeos::tether::TetherComponent::Observer:
   void OnShutdownComplete() override;
 
+  // chromeos::device_sync::DeviceSyncClient::Observer:
+  void OnReady() override;
+
   // Callback when the controlling pref changes.
   void OnPrefsChanged();
 
@@ -136,6 +138,7 @@
  private:
   friend class TetherServiceTest;
   FRIEND_TEST_ALL_PREFIXES(TetherServiceTest, TestSuspend);
+  FRIEND_TEST_ALL_PREFIXES(TetherServiceTest, TestDeviceSyncClientNotReady);
   FRIEND_TEST_ALL_PREFIXES(TetherServiceTest, TestBleAdvertisingNotSupported);
   FRIEND_TEST_ALL_PREFIXES(
       TetherServiceTest,
diff --git a/chrome/browser/chromeos/tether/tether_service_unittest.cc b/chrome/browser/chromeos/tether/tether_service_unittest.cc
index 891a16b..1a7b1226 100644
--- a/chrome/browser/chromeos/tether/tether_service_unittest.cc
+++ b/chrome/browser/chromeos/tether/tether_service_unittest.cc
@@ -261,7 +261,10 @@
   // chromeos::device_sync::DeviceSyncClientImpl::Factory:
   std::unique_ptr<chromeos::device_sync::DeviceSyncClient> BuildInstance(
       service_manager::Connector* connector) override {
-    return std::make_unique<chromeos::device_sync::FakeDeviceSyncClient>();
+    auto fake_device_sync_client =
+        std::make_unique<chromeos::device_sync::FakeDeviceSyncClient>();
+    fake_device_sync_client->NotifyReady();
+    return fake_device_sync_client;
   }
 };
 
@@ -311,6 +314,8 @@
 
     fake_device_sync_client_ =
         std::make_unique<chromeos::device_sync::FakeDeviceSyncClient>();
+    fake_device_sync_client_->NotifyReady();
+
     fake_device_sync_client_impl_factory_ =
         std::make_unique<FakeDeviceSyncClientImplFactory>();
     chromeos::device_sync::DeviceSyncClientImpl::Factory::SetInstanceForTesting(
@@ -415,17 +420,19 @@
         base::WrapUnique(fake_notification_presenter_),
         base::WrapUnique(mock_timer_));
 
-    // Ensure that TetherService does not prematurely update its TechnologyState
-    // before it fetches the BluetoothAdapter.
-    EXPECT_EQ(
-        chromeos::NetworkStateHandler::TechnologyState::TECHNOLOGY_UNAVAILABLE,
-        network_state_handler()->GetTechnologyState(
-            chromeos::NetworkTypePattern::Tether()));
-    VerifyTetherActiveStatus(false /* expected_active */);
-
     SetPrimaryUserLoggedIn();
 
-    base::RunLoop().RunUntilIdle();
+    if (fake_device_sync_client_->is_ready()) {
+      // Ensure that TetherService does not prematurely update its
+      // TechnologyState before it fetches the BluetoothAdapter.
+      EXPECT_EQ(chromeos::NetworkStateHandler::TechnologyState::
+                    TECHNOLOGY_UNAVAILABLE,
+                network_state_handler()->GetTechnologyState(
+                    chromeos::NetworkTypePattern::Tether()));
+      VerifyTetherActiveStatus(false /* expected_active */);
+
+      base::RunLoop().RunUntilIdle();
+    }
   }
 
   void ShutdownTetherService() {
@@ -513,11 +520,11 @@
       fake_tether_host_fetcher_factory_;
   chromeos::tether::FakeNotificationPresenter* fake_notification_presenter_;
   base::MockOneShotTimer* mock_timer_;
-  std::unique_ptr<chromeos::device_sync::DeviceSyncClient>
+  std::unique_ptr<chromeos::device_sync::FakeDeviceSyncClient>
       fake_device_sync_client_;
   std::unique_ptr<FakeDeviceSyncClientImplFactory>
       fake_device_sync_client_impl_factory_;
-  std::unique_ptr<chromeos::secure_channel::SecureChannelClient>
+  std::unique_ptr<chromeos::secure_channel::FakeSecureChannelClient>
       fake_secure_channel_client_;
   std::unique_ptr<FakeSecureChannelClientImplFactory>
       fake_secure_channel_client_impl_factory_;
@@ -625,6 +632,28 @@
       chromeos::tether::TetherComponent::ShutdownReason::USER_CLOSED_LID);
 }
 
+TEST_F(TetherServiceTest, TestDeviceSyncClientNotReady) {
+  base::test::ScopedFeatureList feature_list;
+  feature_list.InitAndEnableFeature(chromeos::features::kMultiDeviceApi);
+
+  fake_device_sync_client_ =
+      std::make_unique<chromeos::device_sync::FakeDeviceSyncClient>();
+
+  CreateTetherService();
+
+  fake_device_sync_client_->NotifyReady();
+  base::RunLoop().RunUntilIdle();
+
+  EXPECT_EQ(chromeos::NetworkStateHandler::TechnologyState::TECHNOLOGY_ENABLED,
+            network_state_handler()->GetTechnologyState(
+                chromeos::NetworkTypePattern::Tether()));
+  VerifyTetherActiveStatus(true /* expected_active */);
+
+  ShutdownTetherService();
+  VerifyLastShutdownReason(
+      chromeos::tether::TetherComponent::ShutdownReason::USER_LOGGED_OUT);
+}
+
 TEST_F(TetherServiceTest, TestBleAdvertisingNotSupported) {
   mock_adapter_->set_is_ble_advertising_supported(false);
 
diff --git a/chrome/browser/conflicts/module_database_win_unittest.cc b/chrome/browser/conflicts/module_database_win_unittest.cc
index 0f9ac58..a405a85 100644
--- a/chrome/browser/conflicts/module_database_win_unittest.cc
+++ b/chrome/browser/conflicts/module_database_win_unittest.cc
@@ -47,6 +47,13 @@
         scoped_testing_local_state_(TestingBrowserProcess::GetGlobal()),
         module_database_(base::SequencedTaskRunnerHandle::Get()) {}
 
+  void TearDown() override {
+    // Give work on background threads a chance run, as it may want to use
+    // |mock_time_task_runner_|, which gets destroyed before
+    // |test_browser_thread_bundle_|
+    test_browser_thread_bundle_.RunUntilIdle();
+  }
+
   const ModuleDatabase::ModuleMap& modules() {
     return module_database_.modules_;
   }
diff --git a/chrome/browser/extensions/api/extension_action/OWNERS b/chrome/browser/extensions/api/extension_action/OWNERS
deleted file mode 100644
index 5b7cd95..0000000
--- a/chrome/browser/extensions/api/extension_action/OWNERS
+++ /dev/null
@@ -1,3 +0,0 @@
-jyasskin@chromium.org
-
-# COMPONENT: Platform>Extensions>API
diff --git a/chrome/browser/extensions/api/web_request/web_request_apitest.cc b/chrome/browser/extensions/api/web_request/web_request_apitest.cc
index 0f9c6748..9aa7aa4a 100644
--- a/chrome/browser/extensions/api/web_request/web_request_apitest.cc
+++ b/chrome/browser/extensions/api/web_request/web_request_apitest.cc
@@ -1605,14 +1605,8 @@
   DISALLOW_COPY_AND_ASSIGN(LocalNTPInterceptionWebRequestAPITest);
 };
 
-// Flaky on Win7x64 and Linux. See https://crbug.com/853118.
-#if defined(OS_WIN) || defined(OS_LINUX)
-#define MAYBE_OneGoogleBarRequestsHidden DISABLED_OneGoogleBarRequestsHidden
-#else
-#define MAYBE_OneGoogleBarRequestsHidden OneGoogleBarRequestsHidden
-#endif
 IN_PROC_BROWSER_TEST_F(LocalNTPInterceptionWebRequestAPITest,
-                       MAYBE_OneGoogleBarRequestsHidden) {
+                       OneGoogleBarRequestsHidden) {
   // Loads an extension which tries to intercept requests to the OneGoogleBar.
   ExtensionTestMessageListener listener("ready", true /*will_reply*/);
   const Extension* extension =
diff --git a/chrome/browser/intranet_redirect_detector.cc b/chrome/browser/intranet_redirect_detector.cc
index 31a7f7f..b7c2862 100644
--- a/chrome/browser/intranet_redirect_detector.cc
+++ b/chrome/browser/intranet_redirect_detector.cc
@@ -48,11 +48,13 @@
                      weak_ptr_factory_.GetWeakPtr()),
       base::TimeDelta::FromSeconds(kStartFetchDelaySeconds));
 
-  net::NetworkChangeNotifier::AddNetworkChangeObserver(this);
+  g_browser_process->network_connection_tracker()->AddNetworkConnectionObserver(
+      this);
 }
 
 IntranetRedirectDetector::~IntranetRedirectDetector() {
-  net::NetworkChangeNotifier::RemoveNetworkChangeObserver(this);
+  g_browser_process->network_connection_tracker()
+      ->RemoveNetworkConnectionObserver(this);
 }
 
 // static
@@ -191,9 +193,9 @@
           redirect_origin_.spec() : std::string());
 }
 
-void IntranetRedirectDetector::OnNetworkChanged(
-    net::NetworkChangeNotifier::ConnectionType type) {
-  if (type == net::NetworkChangeNotifier::CONNECTION_NONE)
+void IntranetRedirectDetector::OnConnectionChanged(
+    network::mojom::ConnectionType type) {
+  if (type == network::mojom::ConnectionType::CONNECTION_NONE)
     return;
   // If a request is already scheduled, do not scheduled yet another one.
   if (in_sleep_)
diff --git a/chrome/browser/intranet_redirect_detector.h b/chrome/browser/intranet_redirect_detector.h
index 7ef5ac4..498b2d9 100644
--- a/chrome/browser/intranet_redirect_detector.h
+++ b/chrome/browser/intranet_redirect_detector.h
@@ -12,9 +12,9 @@
 
 #include "base/macros.h"
 #include "base/memory/weak_ptr.h"
+#include "content/public/browser/network_connection_tracker.h"
 #include "content/public/browser/notification_observer.h"
 #include "content/public/browser/notification_registrar.h"
-#include "net/base/network_change_notifier.h"
 #include "url/gurl.h"
 
 namespace network {
@@ -40,7 +40,7 @@
 // return a value at all times (even during startup or in unittest mode).  If no
 // redirection is in place, the returned GURL will be empty.
 class IntranetRedirectDetector
-    : public net::NetworkChangeNotifier::NetworkChangeObserver {
+    : public content::NetworkConnectionTracker::NetworkConnectionObserver {
  public:
   // Only the main browser process loop should call this, when setting up
   // g_browser_process->intranet_redirect_detector_.  No code other than the
@@ -66,9 +66,8 @@
   void OnSimpleLoaderComplete(network::SimpleURLLoader* source,
                               std::unique_ptr<std::string> response_body);
 
-  // NetworkChangeNotifier::NetworkChangeObserver
-  void OnNetworkChanged(
-      net::NetworkChangeNotifier::ConnectionType type) override;
+  // NetworkConnectionTracker::NetworkConnectionObserver
+  void OnConnectionChanged(network::mojom::ConnectionType type) override;
 
   GURL redirect_origin_;
   std::map<network::SimpleURLLoader*, std::unique_ptr<network::SimpleURLLoader>>
diff --git a/chrome/browser/navigation_predictor/navigation_predictor.cc b/chrome/browser/navigation_predictor/navigation_predictor.cc
index 05ba4ce..46ad3722 100644
--- a/chrome/browser/navigation_predictor/navigation_predictor.cc
+++ b/chrome/browser/navigation_predictor/navigation_predictor.cc
@@ -4,11 +4,18 @@
 
 #include "chrome/browser/navigation_predictor/navigation_predictor.h"
 
+#include "base/logging.h"
 #include "base/metrics/histogram_macros.h"
+#include "chrome/browser/engagement/site_engagement_service.h"
+#include "chrome/browser/profiles/profile.h"
 #include "content/public/browser/render_frame_host.h"
+#include "content/public/browser/site_instance.h"
 #include "mojo/public/cpp/bindings/strong_binding.h"
+#include "url/gurl.h"
 
-NavigationPredictor::NavigationPredictor() {}
+NavigationPredictor::NavigationPredictor(Profile* profile) : profile_(profile) {
+  DCHECK(profile_);
+}
 NavigationPredictor::~NavigationPredictor() = default;
 
 void NavigationPredictor::Create(
@@ -18,15 +25,30 @@
   if (render_frame_host->GetParent())
     return;
 
-  mojo::MakeStrongBinding(std::make_unique<NavigationPredictor>(),
+  Profile* profile = Profile::FromBrowserContext(
+      render_frame_host->GetSiteInstance()->GetBrowserContext());
+
+  mojo::MakeStrongBinding(std::make_unique<NavigationPredictor>(profile),
                           std::move(request));
 }
 
+base::Optional<double> NavigationPredictor::GetEngagementScore(
+    const GURL& url) const {
+  DCHECK(url.SchemeIsHTTPOrHTTPS());
+
+  SiteEngagementService* service = SiteEngagementService::Get(profile_);
+  return service ? base::make_optional(service->GetScore(url)) : base::nullopt;
+}
+
 void NavigationPredictor::UpdateAnchorElementMetrics(
     blink::mojom::AnchorElementMetricsPtr metrics) {
   // TODO(chelu): https://crbug.com/850624/. Use |metrics| to aggregate metrics
   // extracted from the browser process. Analyze and use them to take some
   // actions accordingly.
-  UMA_HISTOGRAM_COUNTS_100("AnchorElementMetrics.Clicked.HrefEngagementScore",
-                           0);
+  auto target_score = GetEngagementScore(metrics->target_url);
+  if (target_score.has_value()) {
+    UMA_HISTOGRAM_COUNTS_100(
+        "AnchorElementMetrics.Clicked.HrefEngagementScore2",
+        static_cast<int>(target_score.value()));
+  }
 }
diff --git a/chrome/browser/navigation_predictor/navigation_predictor.h b/chrome/browser/navigation_predictor/navigation_predictor.h
index 3be64bef..367ecca2 100644
--- a/chrome/browser/navigation_predictor/navigation_predictor.h
+++ b/chrome/browser/navigation_predictor/navigation_predictor.h
@@ -6,6 +6,7 @@
 #define CHROME_BROWSER_NAVIGATION_PREDICTOR_NAVIGATION_PREDICTOR_H_
 
 #include "base/macros.h"
+#include "base/optional.h"
 #include "mojo/public/cpp/bindings/interface_request.h"
 #include "third_party/blink/public/mojom/loader/navigation_predictor.mojom.h"
 
@@ -13,12 +14,15 @@
 class RenderFrameHost;
 }
 
+class GURL;
+class Profile;
+
 // This class gathers metrics of anchor elements from both renderer process
 // and browser process. Then it uses these metrics to make predictions on what
 // are the most likely anchor elements that the user will click.
 class NavigationPredictor : public blink::mojom::AnchorElementMetricsHost {
  public:
-  NavigationPredictor();
+  explicit NavigationPredictor(Profile* profile);
   ~NavigationPredictor() override;
 
   static void Create(mojo::InterfaceRequest<AnchorElementMetricsHost> request,
@@ -29,6 +33,12 @@
       blink::mojom::AnchorElementMetricsPtr metrics) override;
 
  private:
+  // Get site engagement score from SiteEngagementService.
+  base::Optional<double> GetEngagementScore(const GURL& url) const;
+
+  // Browser profile used to retrieve site engagement score.
+  Profile* const profile_;
+
   DISALLOW_COPY_AND_ASSIGN(NavigationPredictor);
 };
 
diff --git a/chrome/browser/navigation_predictor/navigation_predictor_unittest.cc b/chrome/browser/navigation_predictor/navigation_predictor_unittest.cc
index 2eb62e0..e8a492c3 100644
--- a/chrome/browser/navigation_predictor/navigation_predictor_unittest.cc
+++ b/chrome/browser/navigation_predictor/navigation_predictor_unittest.cc
@@ -50,5 +50,5 @@
   base::RunLoop().RunUntilIdle();
 
   histogram_tester.ExpectTotalCount(
-      "AnchorElementMetrics.Clicked.HrefEngagementScore", 1);
+      "AnchorElementMetrics.Clicked.HrefEngagementScore2", 1);
 }
diff --git a/chrome/browser/profiles/profile_attributes_storage.cc b/chrome/browser/profiles/profile_attributes_storage.cc
index 5089205..c4103e8c 100644
--- a/chrome/browser/profiles/profile_attributes_storage.cc
+++ b/chrome/browser/profiles/profile_attributes_storage.cc
@@ -5,6 +5,7 @@
 #include "chrome/browser/profiles/profile_attributes_storage.h"
 
 #include <algorithm>
+#include <utility>
 
 #include "base/bind.h"
 #include "base/files/file_util.h"
@@ -161,12 +162,8 @@
 
 }  // namespace
 
-ProfileAttributesStorage::ProfileAttributesStorage(
-    PrefService* prefs,
-    const base::FilePath& user_data_dir)
+ProfileAttributesStorage::ProfileAttributesStorage(PrefService* prefs)
     : prefs_(prefs),
-      user_data_dir_(user_data_dir),
-      disable_avatar_download_for_testing_(false),
       file_task_runner_(base::CreateSequencedTaskRunnerWithTraits(
           {base::MayBlock(), base::TaskPriority::USER_VISIBLE,
            base::TaskShutdownBehavior::SKIP_ON_SHUTDOWN})) {}
diff --git a/chrome/browser/profiles/profile_attributes_storage.h b/chrome/browser/profiles/profile_attributes_storage.h
index b3bfdcc..9ee85c4 100644
--- a/chrome/browser/profiles/profile_attributes_storage.h
+++ b/chrome/browser/profiles/profile_attributes_storage.h
@@ -40,8 +40,7 @@
  public:
   using Observer = ProfileInfoCacheObserver;
 
-  ProfileAttributesStorage(PrefService* prefs,
-                           const base::FilePath& user_data_dir);
+  explicit ProfileAttributesStorage(PrefService* prefs);
   virtual ~ProfileAttributesStorage();
 
   // If the |supervised_user_id| is non-empty, the profile will be marked to be
@@ -140,8 +139,7 @@
                              const std::string& key,
                              const base::FilePath& image_path);
 
-  PrefService* prefs_;
-  base::FilePath user_data_dir_;
+  PrefService* const prefs_;
   mutable std::unordered_map<base::FilePath::StringType,
                              std::unique_ptr<ProfileAttributesEntry>>
       profile_attributes_entries_;
@@ -167,7 +165,7 @@
 
   // Determines of the ProfileAvatarDownloader should be created and executed
   // or not. Only set to true for tests.
-  bool disable_avatar_download_for_testing_;
+  bool disable_avatar_download_for_testing_ = false;
 
   // Task runner used for file operation on avatar images.
   scoped_refptr<base::SequencedTaskRunner> file_task_runner_;
diff --git a/chrome/browser/profiles/profile_info_cache.cc b/chrome/browser/profiles/profile_info_cache.cc
index 9854772..6818f2a7 100644
--- a/chrome/browser/profiles/profile_info_cache.cc
+++ b/chrome/browser/profiles/profile_info_cache.cc
@@ -68,7 +68,7 @@
 
 ProfileInfoCache::ProfileInfoCache(PrefService* prefs,
                                    const base::FilePath& user_data_dir)
-    : ProfileAttributesStorage(prefs, user_data_dir) {
+    : ProfileAttributesStorage(prefs), user_data_dir_(user_data_dir) {
   // Populate the cache
   DictionaryPrefUpdate update(prefs_, prefs::kProfileInfoCache);
   base::DictionaryValue* cache = update.Get();
diff --git a/chrome/browser/profiles/profile_info_cache.h b/chrome/browser/profiles/profile_info_cache.h
index 571e40e..c7d5bd3 100644
--- a/chrome/browser/profiles/profile_info_cache.h
+++ b/chrome/browser/profiles/profile_info_cache.h
@@ -184,6 +184,7 @@
   void RemoveDeprecatedStatistics();
 
   std::vector<std::string> sorted_keys_;
+  const base::FilePath user_data_dir_;
 
   DISALLOW_COPY_AND_ASSIGN(ProfileInfoCache);
 };
diff --git a/chrome/browser/profiles/profile_window.h b/chrome/browser/profiles/profile_window.h
index 2189f0f1..4b11932b2 100644
--- a/chrome/browser/profiles/profile_window.h
+++ b/chrome/browser/profiles/profile_window.h
@@ -15,7 +15,12 @@
 #include "chrome/browser/ui/profile_chooser_constants.h"
 #include "chrome/browser/ui/startup/startup_types.h"
 
+#if defined(OS_ANDROID)
+#error "Not used on Android"
+#endif
+
 class Profile;
+
 namespace base { class FilePath; }
 
 namespace profiles {
@@ -64,7 +69,6 @@
                                  Profile* profile,
                                  Profile::CreateStatus status);
 
-#if !defined(OS_ANDROID)
 // Loads the specified profile given by |path| asynchronously. Once profile is
 // loaded and initialized it runs |callback| if it isn't null.
 void LoadProfileAsync(const base::FilePath& path,
@@ -81,7 +85,6 @@
 
 // Opens a Browser for the guest profile and runs |callback| if it isn't null.
 void SwitchToGuestProfile(ProfileManager::CreateCallback callback);
-#endif
 
 // Returns true if |profile| has potential profile switch targets, ie there's at
 // least one other profile available to switch to, not counting guest. This is
diff --git a/chrome/browser/profiles/profiles_state.cc b/chrome/browser/profiles/profiles_state.cc
index 8af902c..40d7b4d9 100644
--- a/chrome/browser/profiles/profiles_state.cc
+++ b/chrome/browser/profiles/profiles_state.cc
@@ -67,6 +67,18 @@
   registry->RegisterBooleanPref(prefs::kForceBrowserSignin, false);
 }
 
+void SetLastUsedProfile(const std::string& profile_dir) {
+  // We should never be saving the System Profile as the last one used since it
+  // shouldn't have a browser.
+  if (profile_dir == base::FilePath(chrome::kSystemProfileDir).AsUTF8Unsafe())
+    return;
+
+  PrefService* local_state = g_browser_process->local_state();
+  DCHECK(local_state);
+  local_state->SetString(prefs::kProfileLastUsed, profile_dir);
+}
+
+#if !defined(OS_ANDROID)
 base::string16 GetAvatarNameForProfile(const base::FilePath& profile_path) {
   base::string16 display_name;
 
@@ -160,7 +172,7 @@
 
   return accounts;
 }
-#endif
+#endif  // !defined(OS_CHROMEOS)
 
 bool IsRegularOrGuestSession(Browser* browser) {
   Profile* profile = browser->profile();
@@ -222,7 +234,7 @@
 
   return true;
 }
-#endif
+#endif  // !defined(OS_CHROMEOS)
 
 void RemoveBrowsingDataForProfile(const base::FilePath& profile_path) {
   // The BrowsingDataRemover relies on the ResourceDispatcherHost, which is
@@ -245,17 +257,6 @@
       ChromeBrowsingDataRemoverDelegate::ALL_ORIGIN_TYPES);
 }
 
-void SetLastUsedProfile(const std::string& profile_dir) {
-  // We should never be saving the System Profile as the last one used since it
-  // shouldn't have a browser.
-  if (profile_dir == base::FilePath(chrome::kSystemProfileDir).AsUTF8Unsafe())
-    return;
-
-  PrefService* local_state = g_browser_process->local_state();
-  DCHECK(local_state);
-  local_state->SetString(prefs::kProfileLastUsed, profile_dir);
-}
-
 #if !defined(OS_CHROMEOS)
 bool AreAllNonChildNonSupervisedProfilesLocked() {
   bool at_least_one_regular_profile_present = false;
@@ -287,5 +288,6 @@
 #endif
   return false;
 }
+#endif  // !defined(OS_ANDROID)
 
 }  // namespace profiles
diff --git a/chrome/browser/profiles/profiles_state.h b/chrome/browser/profiles/profiles_state.h
index e4a11b1..4e9d425f 100644
--- a/chrome/browser/profiles/profiles_state.h
+++ b/chrome/browser/profiles/profiles_state.h
@@ -8,9 +8,11 @@
 #include <string>
 
 #include "base/strings/string16.h"
+#include "build/build_config.h"
 
 #if !defined(OS_CHROMEOS)
 #include <vector>
+
 #include "chrome/browser/profiles/avatar_menu.h"
 #endif
 
@@ -18,6 +20,7 @@
 class PrefRegistrySimple;
 class Profile;
 class SigninErrorController;
+
 namespace base { class FilePath; }
 
 namespace profiles {
@@ -36,6 +39,11 @@
 // Register multi-profile related preferences in Local State.
 void RegisterPrefs(PrefRegistrySimple* registry);
 
+// Sets the last used profile pref to |profile_dir|, unless |profile_dir| is the
+// System Profile directory, which is an invalid last used profile.
+void SetLastUsedProfile(const std::string& profile_dir);
+
+#if !defined(OS_ANDROID)
 // Returns the display name of the specified on-the-record profile (or guest),
 // specified by |profile_path|, used in the avatar button or user manager. If
 // |profile_path| is the guest path, it will return IDS_GUEST_PROFILE_NAME. If
@@ -67,7 +75,7 @@
 std::vector<std::string> GetSecondaryAccountsForProfile(
     Profile* profile,
     const std::string& primary_account);
-#endif
+#endif  // !defined(OS_CHROMEOS)
 
 // Returns whether the |browser|'s profile is a non-incognito or guest profile.
 // The distinction is needed because guest profiles are implemented as
@@ -99,16 +107,12 @@
 // profile had been Guest before calling or became Guest as a result of this
 // method.
 bool SetActiveProfileToGuestIfLocked();
-#endif
+#endif  // !defined(OS_CHROMEOS)
 
 // If the profile given by |profile_path| is loaded in the ProfileManager, use
 // a BrowsingDataRemover to delete all the Profile's data.
 void RemoveBrowsingDataForProfile(const base::FilePath& profile_path);
 
-// Sets the last used profile pref to |profile_dir|, unless |profile_dir| is the
-// System Profile directory, which is an invalid last used profile.
-void SetLastUsedProfile(const std::string& profile_dir);
-
 #if !defined(OS_CHROMEOS)
 // Returns true if there exists at least one non-supervised or non-child profile
 // and they are all locked.
@@ -117,6 +121,7 @@
 
 // Returns whether a public session is being run currently.
 bool IsPublicSession();
+#endif  // !defined(OS_ANDROID)
 
 }  // namespace profiles
 
diff --git a/chrome/browser/signin/chrome_signin_client.cc b/chrome/browser/signin/chrome_signin_client.cc
index 40b57fd4..441613a6 100644
--- a/chrome/browser/signin/chrome_signin_client.cc
+++ b/chrome/browser/signin/chrome_signin_client.cc
@@ -22,7 +22,6 @@
 #include "chrome/browser/profiles/profile_attributes_storage.h"
 #include "chrome/browser/profiles/profile_manager.h"
 #include "chrome/browser/profiles/profile_metrics.h"
-#include "chrome/browser/profiles/profile_window.h"
 #include "chrome/browser/signin/account_consistency_mode_manager.h"
 #include "chrome/browser/signin/force_signin_verifier.h"
 #include "chrome/browser/signin/local_auth.h"
@@ -61,12 +60,15 @@
 #include "chrome/browser/chromeos/profiles/profile_helper.h"
 #include "components/user_manager/known_user.h"
 #include "components/user_manager/user_manager.h"
-#else
-#include "chrome/browser/ui/user_manager.h"
 #endif
 
 #if !defined(OS_ANDROID)
 #include "chrome/browser/first_run/first_run.h"
+#include "chrome/browser/profiles/profile_window.h"
+#endif
+
+#if !defined(OS_ANDROID) && !defined(OS_CHROMEOS)
+#include "chrome/browser/ui/user_manager.h"
 #endif
 
 ChromeSigninClient::ChromeSigninClient(
diff --git a/chrome/browser/ui/app_list/arc/arc_app_list_prefs.cc b/chrome/browser/ui/app_list/arc/arc_app_list_prefs.cc
index 32b2309..e208f3c8 100644
--- a/chrome/browser/ui/app_list/arc/arc_app_list_prefs.cc
+++ b/chrome/browser/ui/app_list/arc/arc_app_list_prefs.cc
@@ -902,7 +902,7 @@
                                       : GetAppId(package_name, activity);
 
   // Do not add Play Store app for Public Session and Kiosk modes.
-  if (app_id == arc::kPlayStoreAppId && arc::IsRobotAccountMode())
+  if (app_id == arc::kPlayStoreAppId && arc::IsRobotOrOfflineDemoAccountMode())
     return;
 
   std::string updated_name = name;
diff --git a/chrome/browser/ui/ash/launcher/arc_app_window_launcher_controller.cc b/chrome/browser/ui/ash/launcher/arc_app_window_launcher_controller.cc
index d36abfe..aa6f1c34 100644
--- a/chrome/browser/ui/ash/launcher/arc_app_window_launcher_controller.cc
+++ b/chrome/browser/ui/ash/launcher/arc_app_window_launcher_controller.cc
@@ -493,7 +493,7 @@
     arc::Intent intent;
     if (arc::ParseIntent(app_window_info->launch_intent(), &intent) &&
         intent.HasExtraParam(arc::kInitialStartParam)) {
-      DCHECK(!arc::IsRobotAccountMode());
+      DCHECK(!arc::IsRobotOrOfflineDemoAccountMode());
       arc::UpdatePlayStoreShowTime(
           base::Time::Now() - opt_in_management_check_start_time_,
           owner()->profile());
diff --git a/chrome/browser/ui/ash/login_screen_client.cc b/chrome/browser/ui/ash/login_screen_client.cc
index 15d12966..3c9d622f 100644
--- a/chrome/browser/ui/ash/login_screen_client.cc
+++ b/chrome/browser/ui/ash/login_screen_client.cc
@@ -156,6 +156,10 @@
                                                              false);
 }
 
+void LoginScreenClient::LaunchArcKioskApp(const AccountId& account_id) {
+  chromeos::LoginDisplayHost::default_host()->StartArcKiosk(account_id);
+}
+
 void LoginScreenClient::LoadWallpaper(const AccountId& account_id) {
   WallpaperControllerClient::Get()->ShowUserWallpaper(account_id);
 }
diff --git a/chrome/browser/ui/ash/login_screen_client.h b/chrome/browser/ui/ash/login_screen_client.h
index 8460724c..4fd4e498 100644
--- a/chrome/browser/ui/ash/login_screen_client.h
+++ b/chrome/browser/ui/ash/login_screen_client.h
@@ -84,6 +84,7 @@
                                            const std::string& locale) override;
   void ShowFeedback() override;
   void LaunchKioskApp(const std::string& app_id) override;
+  void LaunchArcKioskApp(const AccountId& account_id) override;
 
  private:
   void SetPublicSessionKeyboardLayout(
diff --git a/chrome/browser/ui/blocked_content/safe_browsing_triggered_popup_blocker.cc b/chrome/browser/ui/blocked_content/safe_browsing_triggered_popup_blocker.cc
index dffe11cc..5dac219d 100644
--- a/chrome/browser/ui/blocked_content/safe_browsing_triggered_popup_blocker.cc
+++ b/chrome/browser/ui/blocked_content/safe_browsing_triggered_popup_blocker.cc
@@ -7,7 +7,6 @@
 #include <utility>
 
 #include "base/memory/ptr_util.h"
-#include "base/metrics/field_trial_params.h"
 #include "base/metrics/histogram_macros.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/common/pref_names.h"
@@ -15,6 +14,7 @@
 #include "components/prefs/pref_service.h"
 #include "components/safe_browsing/db/util.h"
 #include "components/safe_browsing/db/v4_protocol_manager_util.h"
+#include "components/subresource_filter/content/browser/subresource_filter_safe_browsing_activation_throttle.h"
 #include "content/public/browser/navigation_handle.h"
 #include "content/public/browser/page_navigator.h"
 #include "content/public/browser/render_frame_host.h"
@@ -24,21 +24,23 @@
 
 namespace {
 
-const char kIgnoreSublistsParam[] = "ignore_sublists";
-
 void LogAction(SafeBrowsingTriggeredPopupBlocker::Action action) {
   UMA_HISTOGRAM_ENUMERATION("ContentSettings.Popups.StrongBlockerActions",
                             action,
                             SafeBrowsingTriggeredPopupBlocker::Action::kCount);
 }
 
-safe_browsing::SubresourceFilterLevel PickMostSevereLevel(
-    const base::Optional<safe_browsing::SubresourceFilterLevel>& previous,
-    const safe_browsing::SubresourceFilterLevel& current) {
-  if (!previous.has_value()) {
-    return current;
-  }
-  return std::max(current, previous.value());
+subresource_filter::ActivationPosition GetActivationPosition(
+    size_t match_index,
+    size_t num_checks) {
+  DCHECK_GT(num_checks, 0u);
+  if (num_checks == 1)
+    return subresource_filter::ActivationPosition::kOnly;
+  if (match_index == 0)
+    return subresource_filter::ActivationPosition::kFirst;
+  if (match_index == num_checks - 1)
+    return subresource_filter::ActivationPosition::kLast;
+  return subresource_filter::ActivationPosition::kMiddle;
 }
 
 }  // namespace
@@ -111,11 +113,7 @@
     subresource_filter::SubresourceFilterObserverManager* observer_manager)
     : content::WebContentsObserver(web_contents),
       scoped_observer_(this),
-      current_page_data_(std::make_unique<PageData>()),
-      ignore_sublists_(
-          base::GetFieldTrialParamByFeatureAsBool(kAbusiveExperienceEnforce,
-                                                  kIgnoreSublistsParam,
-                                                  false /* default_value */)) {
+      current_page_data_(std::make_unique<PageData>()) {
   DCHECK(observer_manager);
   scoped_observer_.Add(observer_manager);
 }
@@ -157,24 +155,28 @@
     content::NavigationHandle* navigation_handle,
     const SafeBrowsingCheckResults& results) {
   DCHECK(navigation_handle->IsInMainFrame());
-  base::Optional<safe_browsing::SubresourceFilterLevel> current_level;
-  for (const auto& result : results) {
-    if (result.threat_type ==
-        safe_browsing::SBThreatType::SB_THREAT_TYPE_SUBRESOURCE_FILTER) {
-      if (ignore_sublists_) {
-        // No warning for ignore_sublists mode.
-        level_for_next_committed_navigation_ = SubresourceFilterLevel::ENFORCE;
-        return;
-      }
-      auto abusive = result.threat_metadata.subresource_filter_match.find(
-          safe_browsing::SubresourceFilterType::ABUSIVE);
-      if (abusive != result.threat_metadata.subresource_filter_match.end()) {
-        current_level = PickMostSevereLevel(current_level, abusive->second);
-      }
+  base::Optional<safe_browsing::SubresourceFilterLevel> match_level;
+  base::Optional<size_t> match_index;
+  for (size_t i = 0u; i < results.size(); ++i) {
+    const auto& result = results[i];
+    if (result.threat_type !=
+        safe_browsing::SBThreatType::SB_THREAT_TYPE_SUBRESOURCE_FILTER)
+      continue;
+
+    auto abusive = result.threat_metadata.subresource_filter_match.find(
+        safe_browsing::SubresourceFilterType::ABUSIVE);
+    if (abusive != result.threat_metadata.subresource_filter_match.end() &&
+        (!match_level.has_value() || match_level.value() < abusive->second)) {
+      match_level = abusive->second;
+      match_index = i;
     }
   }
-  if (current_level.has_value()) {
-    level_for_next_committed_navigation_ = current_level;
+
+  if (match_level.has_value()) {
+    level_for_next_committed_navigation_ = match_level;
+    UMA_HISTOGRAM_ENUMERATION(
+        "ContentSettings.Popups.StrongBlockerActivationPosition",
+        GetActivationPosition(match_index.value(), results.size()));
   }
 }
 
diff --git a/chrome/browser/ui/blocked_content/safe_browsing_triggered_popup_blocker.h b/chrome/browser/ui/blocked_content/safe_browsing_triggered_popup_blocker.h
index e79bc73..f7a3cb7a 100644
--- a/chrome/browser/ui/blocked_content/safe_browsing_triggered_popup_blocker.h
+++ b/chrome/browser/ui/blocked_content/safe_browsing_triggered_popup_blocker.h
@@ -135,11 +135,6 @@
   // Should never be nullptr.
   std::unique_ptr<PageData> current_page_data_;
 
-  // Whether to ignore the threat pattern type. Useful for flexibility because
-  // we have to wait until metadata patterns reach Stable before using them
-  // without error. Governed by a variation param.
-  bool ignore_sublists_ = false;
-
   DISALLOW_COPY_AND_ASSIGN(SafeBrowsingTriggeredPopupBlocker);
 };
 
diff --git a/chrome/browser/ui/blocked_content/safe_browsing_triggered_popup_blocker_browsertest.cc b/chrome/browser/ui/blocked_content/safe_browsing_triggered_popup_blocker_browsertest.cc
index 9c80c71..dcaee0c0 100644
--- a/chrome/browser/ui/blocked_content/safe_browsing_triggered_popup_blocker_browsertest.cc
+++ b/chrome/browser/ui/blocked_content/safe_browsing_triggered_popup_blocker_browsertest.cc
@@ -200,18 +200,6 @@
   DISALLOW_COPY_AND_ASSIGN(SafeBrowsingTriggeredPopupBlockerBrowserTest);
 };
 
-// The boolean parameter is whether to ignore sublists.
-class SafeBrowsingTriggeredPopupBlockerParamBrowserTest
-    : public SafeBrowsingTriggeredPopupBlockerBrowserTest,
-      public testing::WithParamInterface<bool> {
-  void FinalizeFeatures() override {
-    std::string ignore_param = GetParam() ? "true" : "false";
-    scoped_feature_list_.InitAndEnableFeatureWithParameters(
-        kAbusiveExperienceEnforce, {{"ignore_sublists", ignore_param}});
-  }
-  base::test::ScopedFeatureList scoped_feature_list_;
-};
-
 class SafeBrowsingTriggeredPopupBlockerDisabledTest
     : public SafeBrowsingTriggeredPopupBlockerBrowserTest {
   void FinalizeFeatures() override {
@@ -605,34 +593,6 @@
   open_popup_and_expect_block(false);
 }
 
-IN_PROC_BROWSER_TEST_P(SafeBrowsingTriggeredPopupBlockerParamBrowserTest,
-                       IgnoreSublist) {
-  const char kWindowOpenPath[] = "/subresource_filter/window_open.html";
-  GURL url(embedded_test_server()->GetURL("a.com", kWindowOpenPath));
-
-  // Do not set the ABUSIVE bit.
-  safe_browsing::ThreatMetadata metadata;
-  database_helper()->AddFullHashToDbAndFullHashCache(
-      url, safe_browsing::GetUrlSubresourceFilterId(), metadata);
-
-  // Navigate to url, should not trigger the popup blocker.
-  ui_test_utils::NavigateToURL(browser(), url);
-  bool opened_window = false;
-  EXPECT_TRUE(content::ExecuteScriptAndExtractBool(
-      web_contents(), "openWindow()", &opened_window));
-
-  bool ignoring_sublists = GetParam();
-
-  EXPECT_EQ(ignoring_sublists, !opened_window);
-  EXPECT_EQ(ignoring_sublists,
-            TabSpecificContentSettings::FromWebContents(web_contents())
-                ->IsContentBlocked(CONTENT_SETTINGS_TYPE_POPUPS));
-}
-
-INSTANTIATE_TEST_CASE_P(/* no prefix */,
-                        SafeBrowsingTriggeredPopupBlockerParamBrowserTest,
-                        testing::Values(true, false));
-
 IN_PROC_BROWSER_TEST_F(SafeBrowsingTriggeredPopupBlockerBrowserTest,
                        WarningDoNotBlockCreatingNewWindows_LogsToConsole) {
   const char kWindowOpenPath[] = "/subresource_filter/window_open.html";
diff --git a/chrome/browser/ui/blocked_content/safe_browsing_triggered_popup_blocker_unittest.cc b/chrome/browser/ui/blocked_content/safe_browsing_triggered_popup_blocker_unittest.cc
index e6cbc47c..31e67540 100644
--- a/chrome/browser/ui/blocked_content/safe_browsing_triggered_popup_blocker_unittest.cc
+++ b/chrome/browser/ui/blocked_content/safe_browsing_triggered_popup_blocker_unittest.cc
@@ -19,6 +19,7 @@
 #include "chrome/test/base/testing_browser_process.h"
 #include "components/safe_browsing/db/v4_protocol_manager_util.h"
 #include "components/subresource_filter/content/browser/fake_safe_browsing_database_manager.h"
+#include "components/subresource_filter/content/browser/subresource_filter_safe_browsing_activation_throttle.h"
 #include "components/subresource_filter/core/browser/subresource_filter_features.h"
 #include "content/public/test/navigation_simulator.h"
 #include "content/public/test/test_renderer_host.h"
@@ -143,34 +144,6 @@
   DISALLOW_COPY_AND_ASSIGN(SafeBrowsingTriggeredPopupBlockerTest);
 };
 
-class IgnoreSublistSafeBrowsingTriggeredPopupBlockerTest
-    : public SafeBrowsingTriggeredPopupBlockerTest {
-  std::unique_ptr<base::test::ScopedFeatureList> DefaultFeatureList() override {
-    auto feature_list = std::make_unique<base::test::ScopedFeatureList>();
-    feature_list->InitAndEnableFeatureWithParameters(
-        kAbusiveExperienceEnforce, {{"ignore_sublists", "true"}});
-    return feature_list;
-  }
-};
-
-TEST_F(IgnoreSublistSafeBrowsingTriggeredPopupBlockerTest,
-       MatchNoSublist_BlocksPopup) {
-  const GURL url("https://example.test/");
-  fake_safe_browsing_database()->AddBlacklistedUrl(
-      url, safe_browsing::SB_THREAT_TYPE_SUBRESOURCE_FILTER);
-  NavigateAndCommit(url);
-  EXPECT_TRUE(popup_blocker()->ShouldApplyStrongPopupBlocker(nullptr));
-}
-
-// ignore_sublists should still block on URLs matching a sublist.
-TEST_F(IgnoreSublistSafeBrowsingTriggeredPopupBlockerTest,
-       MatchSublist_BlocksPopup) {
-  const GURL url("https://example.test/");
-  MarkUrlAsAbusiveEnforce(url);
-  NavigateAndCommit(url);
-  EXPECT_TRUE(popup_blocker()->ShouldApplyStrongPopupBlocker(nullptr));
-}
-
 struct RedirectSamplesAndResults {
   GURL initial_url;
   GURL redirect_url;
@@ -429,3 +402,56 @@
 
   EXPECT_FALSE(popup_blocker()->ShouldApplyStrongPopupBlocker(nullptr));
 }
+
+TEST_F(SafeBrowsingTriggeredPopupBlockerTest, ActivationPosition) {
+  const GURL enforce_url("https://enforce.test/");
+  const GURL warn_url("https://warn.test/");
+  MarkUrlAsAbusiveEnforce(enforce_url);
+  MarkUrlAsAbusiveWarning(warn_url);
+
+  using subresource_filter::ActivationPosition;
+  struct {
+    std::vector<const char*> urls;
+    base::Optional<ActivationPosition> expected_position;
+  } kTestCases[] = {
+      {{"https://normal.test/"}, base::nullopt},
+      {{"https://enforce.test/"}, ActivationPosition::kOnly},
+      {{"https://warn.test/"}, ActivationPosition::kOnly},
+
+      {{"https://normal.test/", "https://warn.test/"},
+       ActivationPosition::kLast},
+      {{"https://normal.test/", "https://normal.test/",
+        "https://enforce.test/"},
+       ActivationPosition::kLast},
+
+      {{"https://enforce.test", "https://normal.test/", "https://warn.test/"},
+       ActivationPosition::kFirst},
+      {{"https://warn.test/", "https://normal.test/"},
+       ActivationPosition::kFirst},
+
+      {{"https://normal.test/", "https://enforce.test/",
+        "https://normal.test/"},
+       ActivationPosition::kMiddle},
+  };
+
+  for (const auto& test_case : kTestCases) {
+    base::HistogramTester histograms;
+    const GURL& first_url = GURL(test_case.urls.front());
+    auto navigation_simulator =
+        content::NavigationSimulator::CreateRendererInitiated(first_url,
+                                                              main_rfh());
+    for (size_t i = 1; i < test_case.urls.size(); ++i) {
+      navigation_simulator->Redirect(GURL(test_case.urls[i]));
+    }
+    navigation_simulator->Commit();
+
+    histograms.ExpectTotalCount(
+        "ContentSettings.Popups.StrongBlockerActivationPosition",
+        test_case.expected_position.has_value() ? 1 : 0);
+    if (test_case.expected_position.has_value()) {
+      histograms.ExpectUniqueSample(
+          "ContentSettings.Popups.StrongBlockerActivationPosition",
+          static_cast<int>(test_case.expected_position.value()), 1);
+    }
+  }
+}
diff --git a/chrome/browser/ui/views/accelerator_table.cc b/chrome/browser/ui/views/accelerator_table.cc
index 1bb9442..e0d08c7a 100644
--- a/chrome/browser/ui/views/accelerator_table.cc
+++ b/chrome/browser/ui/views/accelerator_table.cc
@@ -151,6 +151,7 @@
     {ui::VKEY_BROWSER_SEARCH, ui::EF_NONE, IDC_FOCUS_SEARCH},
     {ui::VKEY_M, ui::EF_SHIFT_DOWN | ui::EF_PLATFORM_ACCELERATOR,
      IDC_SHOW_AVATAR_MENU},
+    {ui::VKEY_Q, ui::EF_SHIFT_DOWN | ui::EF_CONTROL_DOWN, IDC_EXIT},
 #endif  // !OS_CHROMEOS
 
 #if defined(GOOGLE_CHROME_BUILD) && !defined(OS_MACOSX)
diff --git a/chrome/browser/ui/views/autofill/save_card_bubble_views_browsertest_base.cc b/chrome/browser/ui/views/autofill/save_card_bubble_views_browsertest_base.cc
index b97a5709..5af93ce 100644
--- a/chrome/browser/ui/views/autofill/save_card_bubble_views_browsertest_base.cc
+++ b/chrome/browser/ui/views/autofill/save_card_bubble_views_browsertest_base.cc
@@ -67,6 +67,17 @@
   url_fetcher_factory_ = std::make_unique<net::FakeURLFetcherFactory>(
       new net::URLFetcherImplFactory());
 
+  // Set up the URL loader factory for the payments client so we can intercept
+  // those network requests too.
+  test_shared_loader_factory_ =
+      base::MakeRefCounted<network::WeakWrapperSharedURLLoaderFactory>(
+          &test_url_loader_factory_);
+  ContentAutofillDriver::GetForRenderFrameHost(
+      GetActiveWebContents()->GetMainFrame())
+      ->autofill_manager()
+      ->payments_client()
+      ->set_url_loader_factory_for_testing(test_shared_loader_factory_);
+
   // Set up this class as the ObserverForTest implementation.
   CreditCardSaveManager* credit_card_save_manager =
       ContentAutofillDriver::GetForRenderFrameHost(
@@ -251,21 +262,19 @@
 }
 
 void SaveCardBubbleViewsBrowserTestBase::SetUploadDetailsRpcPaymentsAccepts() {
-  url_fetcher_factory_->SetFakeResponse(
-      GURL(kURLGetUploadDetailsRequest), kResponseGetUploadDetailsSuccess,
-      net::HTTP_OK, net::URLRequestStatus::SUCCESS);
+  test_url_loader_factory()->AddResponse(kURLGetUploadDetailsRequest,
+                                         kResponseGetUploadDetailsSuccess);
 }
 
 void SaveCardBubbleViewsBrowserTestBase::SetUploadDetailsRpcPaymentsDeclines() {
-  url_fetcher_factory_->SetFakeResponse(
-      GURL(kURLGetUploadDetailsRequest), kResponseGetUploadDetailsFailure,
-      net::HTTP_OK, net::URLRequestStatus::SUCCESS);
+  test_url_loader_factory()->AddResponse(kURLGetUploadDetailsRequest,
+                                         kResponseGetUploadDetailsFailure);
 }
 
 void SaveCardBubbleViewsBrowserTestBase::SetUploadDetailsRpcServerError() {
-  url_fetcher_factory_->SetFakeResponse(
-      GURL(kURLGetUploadDetailsRequest), kResponseGetUploadDetailsSuccess,
-      net::HTTP_INTERNAL_SERVER_ERROR, net::URLRequestStatus::FAILED);
+  test_url_loader_factory()->AddResponse(kURLGetUploadDetailsRequest,
+                                         kResponseGetUploadDetailsSuccess,
+                                         net::HTTP_INTERNAL_SERVER_ERROR);
 }
 
 void SaveCardBubbleViewsBrowserTestBase::ClickOnDialogView(views::View* view) {
@@ -355,4 +364,9 @@
   event_waiter_->Wait();
 }
 
+network::TestURLLoaderFactory*
+SaveCardBubbleViewsBrowserTestBase::test_url_loader_factory() {
+  return &test_url_loader_factory_;
+}
+
 }  // namespace autofill
diff --git a/chrome/browser/ui/views/autofill/save_card_bubble_views_browsertest_base.h b/chrome/browser/ui/views/autofill/save_card_bubble_views_browsertest_base.h
index 489d3aa..e07c4a1 100644
--- a/chrome/browser/ui/views/autofill/save_card_bubble_views_browsertest_base.h
+++ b/chrome/browser/ui/views/autofill/save_card_bubble_views_browsertest_base.h
@@ -20,6 +20,9 @@
 #include "content/public/browser/web_contents_observer.h"
 #include "net/test/embedded_test_server/embedded_test_server.h"
 #include "net/url_request/test_url_fetcher_factory.h"
+#include "services/network/public/cpp/shared_url_loader_factory.h"
+#include "services/network/public/cpp/weak_wrapper_shared_url_loader_factory.h"
+#include "services/network/test/test_url_loader_factory.h"
 #include "testing/gmock/include/gmock/gmock.h"
 
 namespace device {
@@ -101,11 +104,15 @@
   // Wait for the event(s) passed to ResetEventWaiter*() to occur.
   void WaitForObservedEvent();
 
+  network::TestURLLoaderFactory* test_url_loader_factory();
+
   base::test::ScopedFeatureList scoped_feature_list_;
 
  private:
   std::unique_ptr<DialogEventWaiter<DialogEvent>> event_waiter_;
   std::unique_ptr<net::FakeURLFetcherFactory> url_fetcher_factory_;
+  network::TestURLLoaderFactory test_url_loader_factory_;
+  scoped_refptr<network::SharedURLLoaderFactory> test_shared_loader_factory_;
   std::unique_ptr<device::ScopedGeolocationOverrider> geolocation_overrider_;
   const std::string test_file_path_;
 
diff --git a/chrome/browser/ui/views/frame/browser_non_client_frame_view.cc b/chrome/browser/ui/views/frame/browser_non_client_frame_view.cc
index 63cf15e..a8c71a7 100644
--- a/chrome/browser/ui/views/frame/browser_non_client_frame_view.cc
+++ b/chrome/browser/ui/views/frame/browser_non_client_frame_view.cc
@@ -104,7 +104,7 @@
 
 SkColor BrowserNonClientFrameView::GetTabSeparatorColor() const {
   DCHECK(MD::IsRefreshUi());
-  constexpr SkAlpha kTabSeparatorAlpha = 0x6E;  // 43%
+  constexpr SkAlpha kTabSeparatorAlpha = 0x4D;  // 30%
   const SkColor frame_color = GetFrameColor();
   const SkColor base_color =
       color_utils::BlendTowardOppositeLuma(frame_color, SK_AlphaOPAQUE);
diff --git a/chrome/browser/ui/views/media_router/cast_dialog_view.cc b/chrome/browser/ui/views/media_router/cast_dialog_view.cc
index f4a10d69..d24e06520 100644
--- a/chrome/browser/ui/views/media_router/cast_dialog_view.cc
+++ b/chrome/browser/ui/views/media_router/cast_dialog_view.cc
@@ -159,6 +159,8 @@
   }
   dialog_title_ = model.dialog_header();
   MaybeSizeToContents();
+  // Update the main action button.
+  DialogModelChanged();
 }
 
 void CastDialogView::OnControllerInvalidated() {
diff --git a/chrome/browser/ui/views/media_router/cast_dialog_view_unittest.cc b/chrome/browser/ui/views/media_router/cast_dialog_view_unittest.cc
index aa593e9..222cf69 100644
--- a/chrome/browser/ui/views/media_router/cast_dialog_view_unittest.cc
+++ b/chrome/browser/ui/views/media_router/cast_dialog_view_unittest.cc
@@ -26,6 +26,7 @@
 #include "ui/events/base_event_utils.h"
 #include "ui/views/controls/scroll_view.h"
 #include "ui/views/widget/widget.h"
+#include "ui/views/window/dialog_client_view.h"
 
 using testing::_;
 using testing::Invoke;
@@ -289,4 +290,19 @@
   EXPECT_TRUE(scroll_view()->visible());
 }
 
+TEST_F(CastDialogViewTest, SwitchToNoDeviceView) {
+  // Start with one sink. The main button should be enabled.
+  CastDialogModel model = CreateModelWithSinks({CreateAvailableSink()});
+  InitializeDialogWithModel(model);
+  EXPECT_TRUE(dialog_->GetDialogClientView()->ok_button()->enabled());
+
+  // Remove the sink. The no-device view should be shown, and the main button
+  // should be disabled.
+  model.set_media_sinks({});
+  dialog_->OnModelUpdated(model);
+  EXPECT_TRUE(no_sinks_view()->visible());
+  EXPECT_FALSE(scroll_view());
+  EXPECT_FALSE(dialog_->GetDialogClientView()->ok_button()->enabled());
+}
+
 }  // namespace media_router
diff --git a/chrome/browser/ui/views/payments/cvc_unmask_view_controller.cc b/chrome/browser/ui/views/payments/cvc_unmask_view_controller.cc
index 797fe24..1fab783c 100644
--- a/chrome/browser/ui/views/payments/cvc_unmask_view_controller.cc
+++ b/chrome/browser/ui/views/payments/cvc_unmask_view_controller.cc
@@ -26,6 +26,8 @@
 #include "components/payments/core/payment_request_delegate.h"
 #include "components/strings/grit/components_strings.h"
 #include "components/vector_icons/vector_icons.h"
+#include "content/public/browser/browser_context.h"
+#include "content/public/browser/storage_partition.h"
 #include "content/public/browser/web_contents.h"
 #include "ui/base/l10n/l10n_util.h"
 #include "ui/base/resource/resource_bundle.h"
@@ -56,8 +58,9 @@
       credit_card_(credit_card),
       web_contents_(web_contents),
       payments_client_(
-          Profile::FromBrowserContext(web_contents_->GetBrowserContext())
-              ->GetRequestContext(),
+          content::BrowserContext::GetDefaultStoragePartition(
+              web_contents_->GetBrowserContext())
+              ->GetURLLoaderFactoryForBrowserProcess(),
           Profile::FromBrowserContext(web_contents_->GetBrowserContext())
               ->GetPrefs(),
           IdentityManagerFactory::GetForProfile(
diff --git a/chrome/browser/ui/views/tabs/tab_close_button.cc b/chrome/browser/ui/views/tabs/tab_close_button.cc
index ff8fb34..405b12a 100644
--- a/chrome/browser/ui/views/tabs/tab_close_button.cc
+++ b/chrome/browser/ui/views/tabs/tab_close_button.cc
@@ -123,9 +123,8 @@
 void TabCloseButton::Layout() {
   ImageButton::Layout();
   if (focus_ring()) {
-    focus_ring()->Layout();
     SkPath path;
-    path.addOval(gfx::RectToSkRect(GetContentsBounds()));
+    path.addOval(gfx::RectToSkRect(GetMirroredRect(GetContentsBounds())));
     focus_ring()->SetPath(path);
   }
 }
diff --git a/chrome/browser/ui/views/webauthn/sheet_view_factory.cc b/chrome/browser/ui/views/webauthn/sheet_view_factory.cc
index 7863fb0e..043ba13 100644
--- a/chrome/browser/ui/views/webauthn/sheet_view_factory.cc
+++ b/chrome/browser/ui/views/webauthn/sheet_view_factory.cc
@@ -54,6 +54,9 @@
               dialog_model));
       break;
     case Step::kErrorTimedOut:
+      sheet_view = std::make_unique<AuthenticatorRequestSheetView>(
+          std::make_unique<AuthenticatorTimeoutErrorModel>(dialog_model));
+      break;
     case Step::kCompleted:
     case Step::kBlePowerOnAutomatic:
     case Step::kBlePowerOnManual:
diff --git a/chrome/browser/ui/webauthn/authenticator_dialog_browsertest.cc b/chrome/browser/ui/webauthn/authenticator_dialog_browsertest.cc
index d4280302..6db73889 100644
--- a/chrome/browser/ui/webauthn/authenticator_dialog_browsertest.cc
+++ b/chrome/browser/ui/webauthn/authenticator_dialog_browsertest.cc
@@ -40,6 +40,9 @@
     } else if (name == "insert_usb_sign") {
       model->SetCurrentStep(
           AuthenticatorRequestDialogModel::Step::kUsbInsertAndActivateOnSign);
+    } else if (name == "timeout") {
+      model->SetCurrentStep(
+          AuthenticatorRequestDialogModel::Step::kErrorTimedOut);
     }
 
     ShowAuthenticatorRequestDialog(
@@ -68,6 +71,11 @@
 IN_PROC_BROWSER_TEST_F(AuthenticatorDialogTest, InvokeUi_insert_usb_register) {
   ShowAndVerifyUi();
 }
+
 IN_PROC_BROWSER_TEST_F(AuthenticatorDialogTest, InvokeUi_insert_usb_sign) {
   ShowAndVerifyUi();
 }
+
+IN_PROC_BROWSER_TEST_F(AuthenticatorDialogTest, InvokeUi_timeout) {
+  ShowAndVerifyUi();
+}
diff --git a/chrome/browser/ui/webauthn/sheet_models.cc b/chrome/browser/ui/webauthn/sheet_models.cc
index 90494ed2..4ec61bb 100644
--- a/chrome/browser/ui/webauthn/sheet_models.cc
+++ b/chrome/browser/ui/webauthn/sheet_models.cc
@@ -4,6 +4,8 @@
 
 #include "chrome/browser/ui/webauthn/sheet_models.h"
 
+#include <vector>
+
 #include "base/logging.h"
 #include "base/strings/utf_string_conversions.h"
 #include "chrome/grit/generated_resources.h"
@@ -124,3 +126,13 @@
 AuthenticatorInsertAndActivateUsbOnSignSheetModel::GetStepDescription() const {
   return l10n_util::GetStringUTF16(IDS_WEBAUTHN_USB_INSERT_DESCRIPTION);
 }
+
+// AuthenticatorTimeoutErrorModel ---------------------------------------------
+
+base::string16 AuthenticatorTimeoutErrorModel::GetStepTitle() const {
+  return l10n_util::GetStringUTF16(IDS_WEBAUTHN_TIMEOUT_TITLE);
+}
+
+base::string16 AuthenticatorTimeoutErrorModel::GetStepDescription() const {
+  return l10n_util::GetStringUTF16(IDS_WEBAUTHN_TIMEOUT_DESCRIPTION);
+}
diff --git a/chrome/browser/ui/webauthn/sheet_models.h b/chrome/browser/ui/webauthn/sheet_models.h
index 11f8f35..9769d30 100644
--- a/chrome/browser/ui/webauthn/sheet_models.h
+++ b/chrome/browser/ui/webauthn/sheet_models.h
@@ -95,4 +95,14 @@
   base::string16 GetStepDescription() const override;
 };
 
+class AuthenticatorTimeoutErrorModel : public AuthenticatorSheetModelBase {
+ public:
+  using AuthenticatorSheetModelBase::AuthenticatorSheetModelBase;
+
+ private:
+  // AuthenticatorSheetModelBase:
+  base::string16 GetStepTitle() const override;
+  base::string16 GetStepDescription() const override;
+};
+
 #endif  // CHROME_BROWSER_UI_WEBAUTHN_SHEET_MODELS_H_
diff --git a/chrome/browser/vr/gesture_detector.cc b/chrome/browser/vr/gesture_detector.cc
index 11b1def..acaa914e 100644
--- a/chrome/browser/vr/gesture_detector.cc
+++ b/chrome/browser/vr/gesture_detector.cc
@@ -11,11 +11,7 @@
 
 namespace {
 
-constexpr float kDisplacementScaleFactor = 215.0f;
-
-// This varies on the range [0, 1] and represents how much we favor predicted
-// positions over measured positions.
-constexpr float kSmoothingBias = 0.4f;
+constexpr float kDisplacementScaleFactor = 129.0f;
 
 constexpr int kMaxNumOfExtrapolations = 2;
 
@@ -54,8 +50,7 @@
     UpdateOverallVelocity(touch_info);
 
   auto gesture_list = std::make_unique<GestureList>();
-  auto gesture =
-      GetGestureFromTouchInfo(touch_info, force_cancel, current_timestamp);
+  auto gesture = GetGestureFromTouchInfo(touch_info, force_cancel);
   gesture->SetSourceDevice(blink::kWebGestureDeviceTouchpad);
 
   if (gesture->GetType() == blink::WebInputEvent::kGestureScrollEnd)
@@ -68,8 +63,7 @@
 
 std::unique_ptr<blink::WebGestureEvent>
 GestureDetector::GetGestureFromTouchInfo(const TouchInfo& touch_info,
-                                         bool force_cancel,
-                                         base::TimeTicks current_timestamp) {
+                                         bool force_cancel) {
   auto gesture = std::make_unique<blink::WebGestureEvent>();
   gesture->SetTimeStamp(touch_info.touch_point.timestamp);
 
@@ -84,14 +78,12 @@
       break;
     // User is scrolling on touchpad
     case SCROLLING:
-      HandleScrollingState(touch_info, force_cancel, current_timestamp,
-                           gesture.get());
+      HandleScrollingState(touch_info, force_cancel, gesture.get());
       break;
     // The user has finished scrolling, but we'll hallucinate a few points
     // before really finishing.
     case POST_SCROLL:
-      HandlePostScrollingState(touch_info, force_cancel, current_timestamp,
-                               gesture.get());
+      HandlePostScrollingState(touch_info, force_cancel, gesture.get());
       break;
     default:
       NOTREACHED();
@@ -143,7 +135,6 @@
 
 void GestureDetector::HandleScrollingState(const TouchInfo& touch_info,
                                            bool force_cancel,
-                                           base::TimeTicks current_timestamp,
                                            blink::WebGestureEvent* gesture) {
   if (force_cancel) {
     gesture->SetType(blink::WebInputEvent::kGestureScrollEnd);
@@ -156,14 +147,13 @@
   if (touch_position_changed_) {
     gesture->SetType(blink::WebInputEvent::kGestureScrollUpdate);
     UpdateGestureParameters(touch_info);
-    UpdateGestureWithScrollDelta(gesture, current_timestamp);
+    UpdateGestureWithScrollDelta(gesture);
   }
 }
 
 void GestureDetector::HandlePostScrollingState(
     const TouchInfo& touch_info,
     bool force_cancel,
-    base::TimeTicks current_timestamp,
     blink::WebGestureEvent* gesture) {
   if (extrapolated_touch_ == 0 || force_cancel) {
     gesture->SetType(blink::WebInputEvent::kGestureScrollEnd);
@@ -171,40 +161,16 @@
   } else {
     gesture->SetType(blink::WebInputEvent::kGestureScrollUpdate);
     UpdateGestureParameters(touch_info);
-    UpdateGestureWithScrollDelta(gesture, current_timestamp);
+    UpdateGestureWithScrollDelta(gesture);
   }
 }
 
 void GestureDetector::UpdateGestureWithScrollDelta(
-    blink::WebGestureEvent* gesture,
-    base::TimeTicks current_timestamp) {
+    blink::WebGestureEvent* gesture) {
   gesture->data.scroll_update.delta_x =
       state_->displacement.x() * kDisplacementScaleFactor;
   gesture->data.scroll_update.delta_y =
       state_->displacement.y() * kDisplacementScaleFactor;
-
-  // Attempt to smooth the scroll deltas. This depends on a velocity vector.
-  // If we have one, we will use it to compute a predicted scroll and,
-  // ultimately, we will produce a scroll update that blends the predicted and
-  // measured scroll deltas per |kSmoothingBias|.
-  if (!state_->overall_velocity.IsZero()) {
-    gfx::PointF predicted_point;
-    float duration = (current_timestamp - last_timestamp_).InSecondsF();
-    predicted_point.set_x(state_->overall_velocity.x() * duration *
-                          kDisplacementScaleFactor);
-    predicted_point.set_y(state_->overall_velocity.y() * duration *
-                          kDisplacementScaleFactor);
-
-    gfx::PointF measured_point(gesture->data.scroll_update.delta_x,
-                               gesture->data.scroll_update.delta_y);
-
-    gfx::Vector2dF delta =
-        ScaleVector2d(predicted_point - measured_point, kSmoothingBias);
-    gfx::PointF interpolated_point = measured_point + delta;
-
-    gesture->data.scroll_update.delta_x = interpolated_point.x();
-    gesture->data.scroll_update.delta_y = interpolated_point.y();
-  }
 }
 
 bool GestureDetector::UpdateCurrentTouchPoint(const TouchInfo& touch_info) {
diff --git a/chrome/browser/vr/gesture_detector.h b/chrome/browser/vr/gesture_detector.h
index f279730..07df080 100644
--- a/chrome/browser/vr/gesture_detector.h
+++ b/chrome/browser/vr/gesture_detector.h
@@ -62,8 +62,7 @@
 
   std::unique_ptr<blink::WebGestureEvent> GetGestureFromTouchInfo(
       const TouchInfo& input_touch_info,
-      bool force_cancel,
-      base::TimeTicks current_timestamp);
+      bool force_cancel);
 
   void HandleWaitingState(const TouchInfo& touch_info,
                           blink::WebGestureEvent* gesture);
@@ -72,15 +71,12 @@
                             blink::WebGestureEvent* gesture);
   void HandleScrollingState(const TouchInfo& touch_info,
                             bool force_cancel,
-                            base::TimeTicks current_timestamp,
                             blink::WebGestureEvent* gesture);
   void HandlePostScrollingState(const TouchInfo& touch_info,
                                 bool force_cancel,
-                                base::TimeTicks current_timestamp,
                                 blink::WebGestureEvent* gesture);
 
-  void UpdateGestureWithScrollDelta(blink::WebGestureEvent* gesture,
-                                    base::TimeTicks current_timestamp);
+  void UpdateGestureWithScrollDelta(blink::WebGestureEvent* gesture);
 
   // If the user is touching the touch pad and the touch point is different from
   // before, update the touch point and return true. Otherwise, return false.
diff --git a/chrome/browser/webauthn/authenticator_request_dialog_model.cc b/chrome/browser/webauthn/authenticator_request_dialog_model.cc
index 2dc96a3..b0abcb9c 100644
--- a/chrome/browser/webauthn/authenticator_request_dialog_model.cc
+++ b/chrome/browser/webauthn/authenticator_request_dialog_model.cc
@@ -72,3 +72,7 @@
 void AuthenticatorRequestDialogModel::OnRequestComplete() {
   SetCurrentStep(Step::kCompleted);
 }
+
+void AuthenticatorRequestDialogModel::OnRequestTimeout() {
+  SetCurrentStep(Step::kErrorTimedOut);
+}
diff --git a/chrome/browser/webauthn/authenticator_request_dialog_model.h b/chrome/browser/webauthn/authenticator_request_dialog_model.h
index ddd30b3..8138171 100644
--- a/chrome/browser/webauthn/authenticator_request_dialog_model.h
+++ b/chrome/browser/webauthn/authenticator_request_dialog_model.h
@@ -5,6 +5,8 @@
 #ifndef CHROME_BROWSER_WEBAUTHN_AUTHENTICATOR_REQUEST_DIALOG_MODEL_H_
 #define CHROME_BROWSER_WEBAUTHN_AUTHENTICATOR_REQUEST_DIALOG_MODEL_H_
 
+#include <string>
+
 #include "base/observer_list.h"
 #include "chrome/browser/webauthn/transport_list_model.h"
 
@@ -117,6 +119,9 @@
   // To be called when the Web Authentication request is complete.
   void OnRequestComplete();
 
+  // To be called when Web Authentication request times-out.
+  void OnRequestTimeout();
+
  private:
   // The current step of the request UX flow that is currently shown.
   Step current_step_ = Step::kInitial;
diff --git a/chrome/common/thread_profiler.cc b/chrome/common/thread_profiler.cc
index a82a03a..ed9ba92 100644
--- a/chrome/common/thread_profiler.cc
+++ b/chrome/common/thread_profiler.cc
@@ -15,9 +15,7 @@
 #include "base/threading/sequence_local_storage_slot.h"
 #include "base/threading/thread_task_runner_handle.h"
 #include "chrome/common/stack_sampling_configuration.h"
-#include "components/metrics/call_stack_profile_builder.h"
 #include "components/metrics/call_stack_profile_metrics_provider.h"
-#include "components/metrics/call_stack_profile_params.h"
 #include "components/metrics/child_call_stack_profile_collector.h"
 #include "content/public/common/content_switches.h"
 #include "content/public/common/service_names.mojom.h"
@@ -221,7 +219,7 @@
     ScheduleNextPeriodicCollection();
 }
 
-StackSamplingProfiler::CompletedCallback ThreadProfiler::GetReceiverCallback(
+CallStackProfileBuilder::CompletedCallback ThreadProfiler::GetReceiverCallback(
     const CallStackProfileParams& profile_params) {
   // TODO(wittman): Simplify the approach to getting the profiler callback
   // across CallStackProfileMetricsProvider and
@@ -247,14 +245,14 @@
 
 // static
 void ThreadProfiler::ReceiveStartupProfile(
-    const StackSamplingProfiler::CompletedCallback& receiver_callback,
+    const CallStackProfileBuilder::CompletedCallback& receiver_callback,
     StackSamplingProfiler::CallStackProfile profile) {
   receiver_callback.Run(std::move(profile));
 }
 
 // static
 void ThreadProfiler::ReceivePeriodicProfile(
-    const StackSamplingProfiler::CompletedCallback& receiver_callback,
+    const CallStackProfileBuilder::CompletedCallback& receiver_callback,
     scoped_refptr<base::SingleThreadTaskRunner> owning_thread_task_runner,
     base::WeakPtr<ThreadProfiler> thread_profiler,
     StackSamplingProfiler::CallStackProfile profile) {
diff --git a/chrome/common/thread_profiler.h b/chrome/common/thread_profiler.h
index 9d762f9..36862ce7 100644
--- a/chrome/common/thread_profiler.h
+++ b/chrome/common/thread_profiler.h
@@ -15,6 +15,7 @@
 #include "base/threading/thread.h"
 #include "base/threading/thread_checker.h"
 #include "base/time/time.h"
+#include "components/metrics/call_stack_profile_builder.h"
 #include "components/metrics/call_stack_profile_params.h"
 
 namespace service_manager {
@@ -92,19 +93,19 @@
           scoped_refptr<base::SingleThreadTaskRunner>());
 
   // Gets the completed callback for the ultimate receiver of the profile.
-  base::StackSamplingProfiler::CompletedCallback GetReceiverCallback(
+  CallStackProfileBuilder::CompletedCallback GetReceiverCallback(
       const metrics::CallStackProfileParams& profile_params);
 
-  // Receives |profile| from the StackSamplingProfiler and forwards it on to
+  // Receives |profile| from the CallStackProfileBuilder and forwards it on to
   // the original |receiver_callback|.  Note that we must obtain and bind the
   // original receiver callback prior to the start of collection because the
   // collection start time is currently recorded when obtaining the callback in
   // some collection scenarios. The implementation contains a TODO to fix this.
   static void ReceiveStartupProfile(
-      const base::StackSamplingProfiler::CompletedCallback& receiver_callback,
+      const CallStackProfileBuilder::CompletedCallback& receiver_callback,
       base::StackSamplingProfiler::CallStackProfile profile);
   static void ReceivePeriodicProfile(
-      const base::StackSamplingProfiler::CompletedCallback& receiver_callback,
+      const CallStackProfileBuilder::CompletedCallback& receiver_callback,
       scoped_refptr<base::SingleThreadTaskRunner> owning_thread_task_runner,
       base::WeakPtr<ThreadProfiler> thread_profiler,
       base::StackSamplingProfiler::CallStackProfile profile);
diff --git a/chrome/test/chromedriver/chrome/chrome.h b/chrome/test/chromedriver/chrome/chrome.h
index bd0b8f1..bbd50ac1 100644
--- a/chrome/test/chromedriver/chrome/chrome.h
+++ b/chrome/test/chromedriver/chrome/chrome.h
@@ -36,6 +36,16 @@
   // Return the WebView for the given id.
   virtual Status GetWebViewById(const std::string& id, WebView** web_view) = 0;
 
+  // Gets the size of the specified WebView.
+  virtual Status GetWindowSize(const std::string& id,
+                               int* width,
+                               int* height) = 0;
+
+  // Gets the on-screen position of the specified WebView.
+  virtual Status GetWindowPosition(const std::string& target_id,
+                                   int* x,
+                                   int* y) = 0;
+
   // Closes the specified WebView.
   virtual Status CloseWebView(const std::string& id) = 0;
 
diff --git a/chrome/test/chromedriver/chrome/chrome_android_impl.cc b/chrome/test/chromedriver/chrome/chrome_android_impl.cc
index ae3e3b2c..e1c6bda 100644
--- a/chrome/test/chromedriver/chrome/chrome_android_impl.cc
+++ b/chrome/test/chromedriver/chrome/chrome_android_impl.cc
@@ -12,6 +12,7 @@
 #include "chrome/test/chromedriver/chrome/devtools_event_listener.h"
 #include "chrome/test/chromedriver/chrome/devtools_http_client.h"
 #include "chrome/test/chromedriver/chrome/status.h"
+#include "chrome/test/chromedriver/chrome/web_view_impl.h"
 
 ChromeAndroidImpl::ChromeAndroidImpl(
     std::unique_ptr<DevToolsHttpClient> http_client,
@@ -36,6 +37,32 @@
   return "ANDROID";
 }
 
+Status ChromeAndroidImpl::GetWindow(const std::string& target_id,
+                                    Window* window) {
+  WebView* web_view = nullptr;
+  Status status = GetWebViewById(target_id, &web_view);
+  if (status.IsError())
+    return status;
+
+  std::unique_ptr<base::Value> result;
+  std::string expression =
+      "[window.screenX, window.screenY, window.outerWidth * "
+      "window.devicePixelRatio, window.outerHeight * window.devicePixelRatio]";
+  status = web_view->EvaluateScript(target_id, expression, &result);
+  if (status.IsError())
+    return status;
+
+  window->left = result->GetList()[0].GetInt();
+  window->top = result->GetList()[1].GetInt();
+  window->width = result->GetList()[2].GetInt();
+  window->height = result->GetList()[3].GetInt();
+  // Android does not use Window.id or have window states
+  window->id = 0;
+  window->state = "";
+
+  return status;
+}
+
 bool ChromeAndroidImpl::HasTouchScreen() const {
   const BrowserInfo* browser_info = GetBrowserInfo();
   if (browser_info->browser_name == "webview")
diff --git a/chrome/test/chromedriver/chrome/chrome_android_impl.h b/chrome/test/chromedriver/chrome/chrome_android_impl.h
index a68cc85..e373c525 100644
--- a/chrome/test/chromedriver/chrome/chrome_android_impl.h
+++ b/chrome/test/chromedriver/chrome/chrome_android_impl.h
@@ -34,6 +34,9 @@
   bool HasTouchScreen() const override;
   Status QuitImpl() override;
 
+ protected:
+  Status GetWindow(const std::string& target_id, Window* window) override;
+
  private:
   std::unique_ptr<Device> device_;
 };
diff --git a/chrome/test/chromedriver/chrome/chrome_desktop_impl.cc b/chrome/test/chromedriver/chrome/chrome_desktop_impl.cc
index 79927bea..ecce44f3 100644
--- a/chrome/test/chromedriver/chrome/chrome_desktop_impl.cc
+++ b/chrome/test/chromedriver/chrome/chrome_desktop_impl.cc
@@ -231,32 +231,6 @@
   network_connection_ = network_connection;
 }
 
-Status ChromeDesktopImpl::GetWindowPosition(const std::string& target_id,
-                                            int* x,
-                                            int* y) {
-  Window window;
-  Status status = GetWindow(target_id, &window);
-  if (status.IsError())
-    return status;
-
-  *x = window.left;
-  *y = window.top;
-  return Status(kOk);
-}
-
-Status ChromeDesktopImpl::GetWindowSize(const std::string& target_id,
-                                        int* width,
-                                        int* height) {
-  Window window;
-  Status status = GetWindow(target_id, &window);
-  if (status.IsError())
-    return status;
-
-  *width = window.width;
-  *height = window.height;
-  return Status(kOk);
-}
-
 Status ChromeDesktopImpl::SetWindowRect(const std::string& target_id,
                                         const base::DictionaryValue& params) {
   Window window;
@@ -409,55 +383,6 @@
   return SetWindowBounds(window.id, std::move(bounds));
 }
 
-Status ChromeDesktopImpl::ParseWindowBounds(
-    std::unique_ptr<base::DictionaryValue> params,
-    Window* window) {
-  const base::Value* value = nullptr;
-  const base::DictionaryValue* bounds_dict = nullptr;
-  if (!params->Get("bounds", &value) || !value->GetAsDictionary(&bounds_dict))
-    return Status(kUnknownError, "no window bounds in response");
-
-  if (!bounds_dict->GetString("windowState", &window->state))
-    return Status(kUnknownError, "no window state in window bounds");
-
-  if (!bounds_dict->GetInteger("left", &window->left))
-    return Status(kUnknownError, "no left offset in window bounds");
-  if (!bounds_dict->GetInteger("top", &window->top))
-    return Status(kUnknownError, "no top offset in window bounds");
-  if (!bounds_dict->GetInteger("width", &window->width))
-    return Status(kUnknownError, "no width in window bounds");
-  if (!bounds_dict->GetInteger("height", &window->height))
-    return Status(kUnknownError, "no height in window bounds");
-
-  return Status(kOk);
-}
-
-Status ChromeDesktopImpl::ParseWindow(
-    std::unique_ptr<base::DictionaryValue> params,
-    Window* window) {
-  if (!params->GetInteger("windowId", &window->id))
-    return Status(kUnknownError, "no window id in response");
-
-  return ParseWindowBounds(std::move(params), window);
-}
-
-Status ChromeDesktopImpl::GetWindow(const std::string& target_id,
-                                    Window* window) {
-  Status status = devtools_websocket_client_->ConnectIfNecessary();
-  if (status.IsError())
-    return status;
-
-  base::DictionaryValue params;
-  params.SetString("targetId", target_id);
-  std::unique_ptr<base::DictionaryValue> result;
-  status = devtools_websocket_client_->SendCommandAndGetResult(
-      "Browser.getWindowForTarget", params, &result);
-  if (status.IsError())
-    return status;
-
-  return ParseWindow(std::move(result), window);
-}
-
 Status ChromeDesktopImpl::GetWindowBounds(int window_id, Window* window) {
   Status status = devtools_websocket_client_->ConnectIfNecessary();
   if (status.IsError())
diff --git a/chrome/test/chromedriver/chrome/chrome_desktop_impl.h b/chrome/test/chromedriver/chrome/chrome_desktop_impl.h
index 262fe20..2729915 100644
--- a/chrome/test/chromedriver/chrome/chrome_desktop_impl.h
+++ b/chrome/test/chromedriver/chrome/chrome_desktop_impl.h
@@ -12,7 +12,6 @@
 #include "base/command_line.h"
 #include "base/compiler_specific.h"
 #include "base/process/process.h"
-#include "base/values.h"
 #include "chrome/test/chromedriver/chrome/chrome_impl.h"
 #include "chrome/test/chromedriver/chrome/scoped_temp_dir_with_retry.h"
 
@@ -66,8 +65,6 @@
   int GetNetworkConnection() const;
   void SetNetworkConnection(int network_connection);
 
-  Status GetWindowPosition(const std::string& target_id, int* x, int* y);
-  Status GetWindowSize(const std::string& target_id, int* width, int* height);
   Status SetWindowRect(const std::string& target_id,
                        const base::DictionaryValue& params);
   Status SetWindowPosition(const std::string& target_id, int x, int y);
@@ -77,20 +74,6 @@
   Status FullScreenWindow(const std::string& target_id);
 
  private:
-  struct Window {
-    int id;
-    std::string state;
-    int left;
-    int top;
-    int width;
-    int height;
-  };
-  Status ParseWindowBounds(std::unique_ptr<base::DictionaryValue> params,
-                           Window* window);
-  Status ParseWindow(std::unique_ptr<base::DictionaryValue> params,
-                     Window* window);
-
-  Status GetWindow(const std::string& target_id, Window* window);
   Status GetWindowBounds(int window_id, Window* window);
   Status SetWindowBounds(int window_id,
                          std::unique_ptr<base::DictionaryValue> bounds);
diff --git a/chrome/test/chromedriver/chrome/chrome_impl.cc b/chrome/test/chromedriver/chrome/chrome_impl.cc
index b710d1a2..52cd0af 100644
--- a/chrome/test/chromedriver/chrome/chrome_impl.cc
+++ b/chrome/test/chromedriver/chrome/chrome_impl.cc
@@ -122,6 +122,79 @@
   return Status(kUnknownError, "web view not found");
 }
 
+Status ChromeImpl::GetWindow(const std::string& target_id, Window* window) {
+  Status status = devtools_websocket_client_->ConnectIfNecessary();
+  if (status.IsError())
+    return status;
+
+  base::DictionaryValue params;
+  params.SetString("targetId", target_id);
+  std::unique_ptr<base::DictionaryValue> result;
+  status = devtools_websocket_client_->SendCommandAndGetResult(
+      "Browser.getWindowForTarget", params, &result);
+  if (status.IsError())
+    return status;
+
+  return ParseWindow(std::move(result), window);
+}
+
+Status ChromeImpl::GetWindowPosition(const std::string& target_id,
+                                     int* x,
+                                     int* y) {
+  Window window;
+  Status status = GetWindow(target_id, &window);
+  if (status.IsError())
+    return status;
+
+  *x = window.left;
+  *y = window.top;
+  return Status(kOk);
+}
+
+Status ChromeImpl::GetWindowSize(const std::string& target_id,
+                                 int* width,
+                                 int* height) {
+  Window window;
+  Status status = GetWindow(target_id, &window);
+  if (status.IsError())
+    return status;
+
+  *width = window.width;
+  *height = window.height;
+  return Status(kOk);
+}
+
+Status ChromeImpl::ParseWindow(std::unique_ptr<base::DictionaryValue> params,
+                               Window* window) {
+  if (!params->GetInteger("windowId", &window->id))
+    return Status(kUnknownError, "no window id in response");
+
+  return ParseWindowBounds(std::move(params), window);
+}
+
+Status ChromeImpl::ParseWindowBounds(
+    std::unique_ptr<base::DictionaryValue> params,
+    Window* window) {
+  const base::Value* value = nullptr;
+  const base::DictionaryValue* bounds_dict = nullptr;
+  if (!params->Get("bounds", &value) || !value->GetAsDictionary(&bounds_dict))
+    return Status(kUnknownError, "no window bounds in response");
+
+  if (!bounds_dict->GetString("windowState", &window->state))
+    return Status(kUnknownError, "no window state in window bounds");
+
+  if (!bounds_dict->GetInteger("left", &window->left))
+    return Status(kUnknownError, "no left offset in window bounds");
+  if (!bounds_dict->GetInteger("top", &window->top))
+    return Status(kUnknownError, "no top offset in window bounds");
+  if (!bounds_dict->GetInteger("width", &window->width))
+    return Status(kUnknownError, "no width in window bounds");
+  if (!bounds_dict->GetInteger("height", &window->height))
+    return Status(kUnknownError, "no height in window bounds");
+
+  return Status(kOk);
+}
+
 Status ChromeImpl::CloseWebView(const std::string& id) {
   Status status = devtools_http_client_->CloseWebView(id);
   if (status.IsError())
diff --git a/chrome/test/chromedriver/chrome/chrome_impl.h b/chrome/test/chromedriver/chrome/chrome_impl.h
index c4d1cc87..05fbdf5 100644
--- a/chrome/test/chromedriver/chrome/chrome_impl.h
+++ b/chrome/test/chromedriver/chrome/chrome_impl.h
@@ -12,6 +12,7 @@
 
 #include "base/compiler_specific.h"
 #include "base/memory/linked_ptr.h"
+#include "base/values.h"
 #include "chrome/test/chromedriver/chrome/chrome.h"
 
 struct BrowserInfo;
@@ -26,7 +27,6 @@
 class ChromeImpl : public Chrome {
  public:
   ~ChromeImpl() override;
-
   // Overridden from Chrome:
   Status GetAsDesktop(ChromeDesktopImpl** desktop) override;
   const BrowserInfo* GetBrowserInfo() const override;
@@ -36,6 +36,8 @@
   Status GetWebViewIds(std::list<std::string>* web_view_ids,
                        bool w3c_compliant) override;
   Status GetWebViewById(const std::string& id, WebView** web_view) override;
+  Status GetWindowSize(const std::string& id, int* width, int* height) override;
+  Status GetWindowPosition(const std::string& id, int* x, int* y) override;
   Status CloseWebView(const std::string& id) override;
   Status ActivateWebView(const std::string& id) override;
   Status SetAcceptInsecureCerts() override;
@@ -53,6 +55,20 @@
 
   virtual Status QuitImpl() = 0;
 
+  struct Window {
+    int id;
+    std::string state;
+    int left;
+    int top;
+    int width;
+    int height;
+  };
+  virtual Status GetWindow(const std::string& target_id, Window* window);
+  Status ParseWindow(std::unique_ptr<base::DictionaryValue> params,
+                     Window* window);
+  Status ParseWindowBounds(std::unique_ptr<base::DictionaryValue> params,
+                           Window* window);
+
   bool quit_;
   std::unique_ptr<DevToolsHttpClient> devtools_http_client_;
   std::unique_ptr<DevToolsClient> devtools_websocket_client_;
diff --git a/chrome/test/chromedriver/chrome/stub_chrome.cc b/chrome/test/chromedriver/chrome/stub_chrome.cc
index 390a68eb..7a59770 100644
--- a/chrome/test/chromedriver/chrome/stub_chrome.cc
+++ b/chrome/test/chromedriver/chrome/stub_chrome.cc
@@ -36,6 +36,18 @@
   return Status(kOk);
 }
 
+Status StubChrome::GetWindowSize(const std::string& id,
+                                 int* width,
+                                 int* height) {
+  return Status(kOk);
+}
+
+Status StubChrome::GetWindowPosition(const std::string& target_id,
+                                     int* x,
+                                     int* y) {
+  return Status(kOk);
+}
+
 Status StubChrome::CloseWebView(const std::string& id) {
   return Status(kOk);
 }
diff --git a/chrome/test/chromedriver/chrome/stub_chrome.h b/chrome/test/chromedriver/chrome/stub_chrome.h
index c74af2c..bd78778 100644
--- a/chrome/test/chromedriver/chrome/stub_chrome.h
+++ b/chrome/test/chromedriver/chrome/stub_chrome.h
@@ -29,6 +29,10 @@
   Status GetWebViewIds(std::list<std::string>* web_view_ids,
                        bool w3c_compliant) override;
   Status GetWebViewById(const std::string& id, WebView** web_view) override;
+  Status GetWindowSize(const std::string& id, int* width, int* height) override;
+  Status GetWindowPosition(const std::string& target_id,
+                           int* x,
+                           int* y) override;
   Status CloseWebView(const std::string& id) override;
   Status ActivateWebView(const std::string& id) override;
   Status SetAcceptInsecureCerts() override;
diff --git a/chrome/test/chromedriver/session_commands.cc b/chrome/test/chromedriver/session_commands.cc
index f3d12db..18ad124 100644
--- a/chrome/test/chromedriver/session_commands.cc
+++ b/chrome/test/chromedriver/session_commands.cc
@@ -750,34 +750,21 @@
   return Status(kOk);
 }
 
+// TODO(johnchen): There is no public method in Chrome or ChromeDesktopImpl to
+// get both size and position in one call. What we're doing now is kind of
+// wasteful, since both GetWindowPosition and GetWindowSize end up getting both
+// position and size, and then discard one of the two pieces.
 Status ExecuteGetWindowRect(Session* session,
                             const base::DictionaryValue& params,
                             std::unique_ptr<base::Value>* value) {
-  ChromeDesktopImpl* desktop = NULL;
-  Status status = session->chrome->GetAsDesktop(&desktop);
-  if (status.IsError())
-    return status;
-
   int x, y;
   int width, height;
 
-  if (desktop->GetBrowserInfo()->build_no >= kBrowserWindowDevtoolsBuildNo) {
-    status = desktop->GetWindowPosition(session->window, &x, &y);
-    if (status.IsError())
-      return status;
-    status = desktop->GetWindowSize(session->window, &width, &height);
-  } else {
-    AutomationExtension* extension = NULL;
-    status =
-        desktop->GetAutomationExtension(&extension, session->w3c_compliant);
-    if (status.IsError())
-      return status;
+  Status status = session->chrome->GetWindowPosition(session->window, &x, &y);
+  if (status.IsError())
+    return status;
+  status = session->chrome->GetWindowSize(session->window, &width, &height);
 
-    status = extension->GetWindowPosition(&x, &y);
-    if (status.IsError())
-      return status;
-    status = extension->GetWindowSize(&width, &height);
-  }
   if (status.IsError())
     return status;
 
@@ -793,24 +780,9 @@
 Status ExecuteGetWindowPosition(Session* session,
                                 const base::DictionaryValue& params,
                                 std::unique_ptr<base::Value>* value) {
-  ChromeDesktopImpl* desktop = NULL;
-  Status status = session->chrome->GetAsDesktop(&desktop);
-  if (status.IsError())
-    return status;
-
   int x, y;
+  Status status = session->chrome->GetWindowPosition(session->window, &x, &y);
 
-  if (desktop->GetBrowserInfo()->build_no >= kBrowserWindowDevtoolsBuildNo) {
-    status = desktop->GetWindowPosition(session->window, &x, &y);
-  } else {
-    AutomationExtension* extension = NULL;
-    status =
-        desktop->GetAutomationExtension(&extension, session->w3c_compliant);
-    if (status.IsError())
-      return status;
-
-    status = extension->GetWindowPosition(&x, &y);
-  }
   if (status.IsError())
     return status;
 
@@ -850,24 +822,10 @@
 Status ExecuteGetWindowSize(Session* session,
                             const base::DictionaryValue& params,
                             std::unique_ptr<base::Value>* value) {
-  ChromeDesktopImpl* desktop = NULL;
-  Status status = session->chrome->GetAsDesktop(&desktop);
-  if (status.IsError())
-    return status;
-
   int width, height;
 
-  if (desktop->GetBrowserInfo()->build_no >= kBrowserWindowDevtoolsBuildNo) {
-    status = desktop->GetWindowSize(session->window, &width, &height);
-  } else {
-    AutomationExtension* extension = NULL;
-    status =
-        desktop->GetAutomationExtension(&extension, session->w3c_compliant);
-    if (status.IsError())
-      return status;
-
-    status = extension->GetWindowSize(&width, &height);
-  }
+  Status status =
+      session->chrome->GetWindowSize(session->window, &width, &height);
   if (status.IsError())
     return status;
 
diff --git a/chrome/test/chromedriver/test/run_py_tests.py b/chrome/test/chromedriver/test/run_py_tests.py
index c29fb5e..90e269d 100755
--- a/chrome/test/chromedriver/test/run_py_tests.py
+++ b/chrome/test/chromedriver/test/run_py_tests.py
@@ -180,7 +180,7 @@
         'ChromeDownloadDirTest.*',
         # https://crbug.com/274650
         'ChromeDriverTest.testCloseWindow',
-        # https://bugs.chromium.org/p/chromedriver/issues/detail?id=298
+        # Most window operations don't make sense on Android.
         'ChromeDriverTest.testWindowFullScreen',
         'ChromeDriverTest.testWindowPosition',
         'ChromeDriverTest.testWindowSize',
@@ -1923,6 +1923,24 @@
     orientation = self._driver.GetScreenOrientation()
     self.assertEqual(orientation['orientation'], 'LANDSCAPE')
 
+  def testAndroidGetWindowSize(self):
+    self._driver = self.CreateDriver()
+    size = self._driver.GetWindowSize()
+
+    script_size = self._driver.ExecuteScript(
+      "return [window.outerWidth * window.devicePixelRatio,"
+      "window.outerHeight * window.devicePixelRatio]")
+    self.assertEquals(size, script_size)
+
+    script_inner = self._driver.ExecuteScript(
+      "return [window.innerWidth, window.innerHeight]")
+    self.assertLessEqual(script_inner[0], size[0])
+    self.assertLessEqual(script_inner[1], size[1])
+    # Sanity check: screen dimensions in the range 2-20000px
+    self.assertLessEqual(size[0], 20000)
+    self.assertLessEqual(size[1], 20000)
+    self.assertGreaterEqual(size[0], 2)
+    self.assertGreaterEqual(size[1], 2)
 
 class ChromeDownloadDirTest(ChromeDriverBaseTest):
 
diff --git a/chromecast/base/java/src/org/chromium/chromecast/base/Both.java b/chromecast/base/java/src/org/chromium/chromecast/base/Both.java
index 00991c0..18cfb10 100644
--- a/chromecast/base/java/src/org/chromium/chromecast/base/Both.java
+++ b/chromecast/base/java/src/org/chromium/chromecast/base/Both.java
@@ -57,21 +57,22 @@
     /**
      * Turns a function of two arguments into a function of a single Both argument.
      */
-    public static <A, B, R> Function<Both<A, B>, R> adapt(BiFunction<A, B, R> function) {
+    public static <A, B, R> Function<Both<A, B>, R> adapt(
+            BiFunction<? super A, ? super B, ? extends R> function) {
         return (Both<A, B> data) -> function.apply(data.first, data.second);
     }
 
     /**
      * Turns a consumer of two arguments into a consumer of a single Both argument.
      */
-    public static <A, B> Consumer<Both<A, B>> adapt(BiConsumer<A, B> consumer) {
+    public static <A, B> Consumer<Both<A, B>> adapt(BiConsumer<? super A, ? super B> consumer) {
         return (Both<A, B> data) -> consumer.accept(data.first, data.second);
     }
 
     /**
      * Turns a predicate of two arguments into a predicate of a single Both argument.
      */
-    public static <A, B> Predicate<Both<A, B>> adapt(BiPredicate<A, B> predicate) {
+    public static <A, B> Predicate<Both<A, B>> adapt(BiPredicate<? super A, ? super B> predicate) {
         return (Both<A, B> data) -> predicate.test(data.first, data.second);
     }
 }
diff --git a/chromecast/base/java/src/org/chromium/chromecast/base/Observable.java b/chromecast/base/java/src/org/chromium/chromecast/base/Observable.java
index f7603a73..f468b9a5 100644
--- a/chromecast/base/java/src/org/chromium/chromecast/base/Observable.java
+++ b/chromecast/base/java/src/org/chromium/chromecast/base/Observable.java
@@ -103,61 +103,6 @@
     }
 
     /**
-     * Returns an Observable that is activated only when `this` is first activated, and is not
-     * activated an subsequent activations of `this`.
-     *
-     * This is useful for ensuring that a callback registered with watch() is only run once.
-     */
-    public final Observable<T> first() {
-        return new FirstActivationStateObserver<>(this).asObservable();
-    }
-
-    /**
-     * Returns an Observable that is activated when `this` is activated any time besides the first,
-     * and provides as activation data a `Both` object containing the previous and new activation
-     * data of `this`.
-     *
-     * This is useful if registered callbacks need to know the data of the previous activation.
-     */
-    public final Observable<Both<T, T>> changes() {
-        return new ChangeStateObserver<>(this).asObservable();
-    }
-
-    /**
-     * Returns an Observable that does not activate if `this` is set with a value such that the
-     * given predicate returns true for the previous value and the current value.
-     *
-     * Can be used to ignore repeat activations that contain the same data. Beware that even though
-     * a repeat activation that passes the given predicate will not re-activate the new Observable,
-     * it will deactivate it.
-     */
-    public final Observable<T> unique(BiPredicate<? super T, ? super T> predicate) {
-        Controller<T> controller = new Controller<>();
-        ScopeFactory<T> pipeToController = (T value) -> {
-            controller.set(value);
-            return controller::reset;
-        };
-        first().watch(pipeToController);
-        changes()
-                .filter(Both.adapt((T a, T b) -> !predicate.test(a, b)))
-                .map(Both::getSecond)
-                .watch(pipeToController);
-        return controller;
-    }
-
-    /**
-     * Returns an Observable that does not activate if `this` is activated with a value that is
-     * equal to the data of a previous activation, according to that data's `equals()` method.
-     *
-     * Can be used to ignore repeat activations that contain the same data. Beware that even though
-     * a repeat activation that passes the given predicate will not re-activate the new Observable,
-     * it will deactivate it.
-     */
-    public final Observable<T> unique() {
-        return unique(Object::equals);
-    }
-
-    /**
      * Returns an Observable that is activated only when the given Observable is not activated.
      */
     public static Observable<Unit> not(Observable<?> observable) {
@@ -197,45 +142,4 @@
             return mController;
         }
     }
-
-    // Owns a Controller that is activated only on the Observable's first activation.
-    private static class FirstActivationStateObserver<T> {
-        private final Controller<T> mController = new Controller<>();
-        private boolean mIsActivated = false;
-
-        private FirstActivationStateObserver(Observable<T> state) {
-            state.watch((T value) -> {
-                if (!mIsActivated) {
-                    mController.set(value);
-                    mIsActivated = true;
-                }
-                return mController::reset;
-            });
-        }
-
-        private Observable<T> asObservable() {
-            return mController;
-        }
-    }
-
-    // Owns a Controller that is activated on non-first activations with the previous and new
-    // activation data.
-    private static class ChangeStateObserver<T> {
-        private final Controller<Both<T, T>> mController = new Controller<>();
-        private T mCurrent = null;
-
-        private ChangeStateObserver(Observable<T> state) {
-            state.watch((T value) -> {
-                if (mCurrent != null) {
-                    mController.set(Both.both(mCurrent, value));
-                }
-                mCurrent = value;
-                return mController::reset;
-            });
-        }
-
-        private Observable<Both<T, T>> asObservable() {
-            return mController;
-        }
-    }
 }
diff --git a/chromecast/base/java/test/org/chromium/chromecast/base/BothTest.java b/chromecast/base/java/test/org/chromium/chromecast/base/BothTest.java
index 21dcbf0..d344c69 100644
--- a/chromecast/base/java/test/org/chromium/chromecast/base/BothTest.java
+++ b/chromecast/base/java/test/org/chromium/chromecast/base/BothTest.java
@@ -14,6 +14,9 @@
 import org.junit.runner.RunWith;
 import org.junit.runners.BlockJUnit4ClassRunner;
 
+import org.chromium.chromecast.base.Inheritance.Base;
+import org.chromium.chromecast.base.Inheritance.Derived;
+
 import java.util.ArrayList;
 import java.util.List;
 
@@ -67,6 +70,21 @@
     }
 
     @Test
+    public void testAdaptBiFunctionBaseArguments() {
+        // Compile error if generics are wrong.
+        Function<Both<Derived, Derived>, String> f = Both.adapt((Base a, Base b) -> "success");
+        assertEquals(f.apply(Both.both(new Derived(), new Derived())), "success");
+    }
+
+    @Test
+    public void testAdaptBiFunctionDerivedResult() {
+        // Compile error if generics are wrong.
+        Derived derived = new Derived();
+        Function<Both<String, String>, Base> f = Both.adapt((String a, String b) -> derived);
+        assertEquals(f.apply(Both.both("a", "b")), derived);
+    }
+
+    @Test
     public void testAdaptBiConsumer() {
         List<String> result = new ArrayList<>();
         Both.adapt((String a, String b) -> { result.add(a + b); }).accept(Both.both("A", "B"));
@@ -74,9 +92,31 @@
     }
 
     @Test
+    public void testAdaptBiConsumerBaseArguments() {
+        // Compile error if generics are wrong.
+        List<String> result = new ArrayList<>();
+        Consumer<Both<Derived, Derived>> c =
+                Both.adapt((Base a, Base b) -> { result.add("success"); });
+        c.accept(Both.both(new Derived(), new Derived()));
+        assertThat(result, contains("success"));
+    }
+
+    @Test
     public void testAdaptBiPredicate() {
         Predicate<Both<String, String>> p = Both.adapt(String::equals);
         assertTrue(p.test(Both.both("a", "a")));
         assertFalse(p.test(Both.both("a", "b")));
     }
+
+    @Test
+    public void testAdaptBiPredicateBaseArguments() {
+        // Compile error if generics are wrong.
+        Predicate<Both<Derived, Derived>> p = Both.adapt((Base a, Base b) -> a.equals(b));
+        Derived derived1 = new Derived();
+        Derived derived2 = new Derived();
+        assertTrue(p.test(Both.both(derived1, derived1)));
+        assertTrue(p.test(Both.both(derived2, derived2)));
+        assertFalse(p.test(Both.both(derived1, derived2)));
+        assertFalse(p.test(Both.both(derived2, derived1)));
+    }
 }
diff --git a/chromecast/base/java/test/org/chromium/chromecast/base/ObservableAndControllerTest.java b/chromecast/base/java/test/org/chromium/chromecast/base/ObservableAndControllerTest.java
index 4d483a1..6df05487 100644
--- a/chromecast/base/java/test/org/chromium/chromecast/base/ObservableAndControllerTest.java
+++ b/chromecast/base/java/test/org/chromium/chromecast/base/ObservableAndControllerTest.java
@@ -278,72 +278,6 @@
     }
 
     @Test
-    public void testFirst() {
-        Controller<String> a = new Controller<>();
-        List<String> result = new ArrayList<>();
-        a.first().watch(report(result, "first"));
-        a.set("first");
-        a.set("second");
-        assertThat(result, contains("enter first: first", "exit first"));
-    }
-
-    @Test
-    public void testChanges() {
-        Controller<String> a = new Controller<>();
-        List<String> result = new ArrayList<>();
-        a.changes().watch(report(result, "changes"));
-        a.set("first");
-        a.set("second");
-        a.set("third");
-        assertThat(result,
-                contains("enter changes: first, second", "exit changes",
-                        "enter changes: second, third"));
-    }
-
-    @Test
-    public void testChangesIsResetWhenSourceIsReset() {
-        Controller<String> a = new Controller<>();
-        List<String> result = new ArrayList<>();
-        a.changes().watch(report(result, "changes"));
-        a.set("first");
-        a.set("second");
-        a.reset();
-        assertThat(result, contains("enter changes: first, second", "exit changes"));
-    }
-
-    @Test
-    public void testUniqueDefault() {
-        Controller<String> a = new Controller<>();
-        List<String> result = new ArrayList<>();
-        a.unique().watch(report(result, "unique"));
-        a.set("hi");
-        a.set("ho");
-        a.set("hey");
-        a.set("hey");
-        a.set("hey");
-        a.set("hi");
-        assertThat(result,
-                contains("enter unique: hi", "exit unique", "enter unique: ho", "exit unique",
-                        "enter unique: hey", "exit unique", "enter unique: hi"));
-    }
-
-    @Test
-    public void testUniqueWithCustomPredicate() {
-        Controller<String> a = new Controller<>();
-        List<String> result = new ArrayList<>();
-        a.unique((p, s) -> p.equalsIgnoreCase(s)).watch(report(result, "unique ignore case"));
-        a.set("kale");
-        a.set("KALE");
-        a.set("steamed kale");
-        a.set("STEAMED");
-        a.set("sTeAmEd");
-        assertThat(result,
-                contains("enter unique ignore case: kale", "exit unique ignore case",
-                        "enter unique ignore case: steamed kale", "exit unique ignore case",
-                        "enter unique ignore case: STEAMED", "exit unique ignore case"));
-    }
-
-    @Test
     public void testMap() {
         Controller<String> original = new Controller<>();
         Observable<String> lowerCase = original.map(String::toLowerCase);
@@ -363,6 +297,15 @@
     }
 
     @Test
+    public void testMapDropsNullResult() {
+        Controller<Unit> controller = new Controller<>();
+        ReactiveRecorder recorder = ReactiveRecorder.record(controller.map(x -> null));
+        controller.set(Unit.unit());
+        // Recorder should not get any events because the map function returned null.
+        recorder.verify().end();
+    }
+
+    @Test
     public void testFilter() {
         Controller<String> a = new Controller<>();
         List<String> result = new ArrayList<>();
diff --git a/chromecast/base/reactive_java.md b/chromecast/base/reactive_java.md
index b2760b4aa..a162d840 100644
--- a/chromecast/base/reactive_java.md
+++ b/chromecast/base/reactive_java.md
@@ -510,151 +510,6 @@
 on the transition between `(just A)` and `(A and then B)`, and will not activate
 on the transition between `(just B)` and `(B and then A)`.
 
-### One-time callbacks with `first()`
-
-Sometimes you want to ensure that an action is only performed once, even if it's
-triggered by something that might happen more than once. With the `first()`
-method, doing so is a breeze:
-
-```java
-    Controller<Unit> windowFocusState = new Controller<>();
-    windowFocusState.first().watch(ScopeFactories.onEnter(this:: createWindow);
-    windowFocusState.set(Unit.unit()); // Will invoke createWindow()
-    windowFocusState.reset();
-    windowFocusState.set(Unit.unit()); // Does not invoke createWindow()
-```
-
-Note that the `Observable` created by `first()` will be deactivated the first
-time that the source `Observable` is deactivated:
-
-```java
-    Controller<String> messageState = new Controller<>();
-    messageState.first().watch(message -> {
-        Log.d(TAG, "first message: " + message);
-        return () -> Log.d(TAG, "first message discarded");
-    });
-    messageState.set("hello"); // Logs "first message: hello"
-    messageState.reset(); // Logs "first message discarded"
-    messageState.set("hello?"); // Nothing gets logged
-```
-
-### Compare old and new activation values with `changes()`
-
-One drawback of the `watch()` method is that it provides only the activation
-data with no other context. This is usually a good thing in that it helps with
-encapsulation, but there are a number of applications where information about
-the *previous activation* is useful. For example, say we want to log the delta
-between volume levels:
-
-```java
-    private final Controller<Integer> mVolumeLevel = new Controller<>();
-
-    private void logVolumeChanges(int oldLevel, int newLevel) {
-        if (newLevel > oldLevel) {
-            Log.d(TAG, "Volume increased by " + (newLevel - oldLevel));
-        } else if (newLevel < oldLevel) {
-            Log.d(TAG, "Volume decreased by " + (oldLevel - newLevel));
-        }
-    }
-
-    {
-        // How do we react to mVolumeLevel with logVolumeChanges()?
-        mVolumeLevel.set(0);
-    }
-
-    public void setVolume(int volume) {
-        mVolumeLevel.set(volume);
-    }
-```
-
-The problem here is that if you `watch()` `mVolumeLevel`, you will know the new
-volume, but not the old volume, so a stateless lambda will not know enough to
-call `logVolumeChanges()`.
-
-If you tried, you'd probably have to end up creating a custom `ScopeFactory`
-implementation that stores some internal state that is changed when activated.
-
-But fortunately, an easier way is provided that doesn't force you to put state
-into your `ScopeFactory`: the `changes()` method.
-
-When you call `changes()` on an `Observable`, the resulting `Observable` will
-be activated with a `Both` object containing the previous and new activation
-data. Here's how you would use it in the above example:
-
-```java
-    {
-        Observable<Both<Integer, Integer>> volumeChanges =
-                mVolumeLevel.changes()
-        volumeChanges.watch(ScopeFactories.onEnter((oldLevel, newLevel) -> {
-            logVolumeChanges(oldLevel, newLevel);
-        }));
-    }
-```
-
-Note: the above is using the `BiConsumer` version of `ScopeFactories.onEnter()`,
-so it creates a `ScopeFactory<Both<Integer, Integer>>`, which is just the type
-that we need. More info is given in the section on `ScopeFactories`.
-
-### Ignore duplicates with `unique()`
-
-Consider this:
-
-```java
-    Controller<Intege> volumeLevel = new Controller<>();
-    volumeLevel.watch(ScopeFactories.onEnter(level -> {
-        Log.d(TAG, "New volume level: " + level);
-    }));
-    volumeLevel.set(3); // Logs "New volume level: 3"
-    volumeLevel.set(3); // Logs "New volume level: 3"... again
-    volumeLevel.set(3); // Logs "New volume level: 3"... yet again
-    volumeLevel.set(4); // Logs "New volume level: 4"... finally something new!
-```
-
-What if we only care about *changes* to the volume? Well, we could modify the
-above section on `changes()` a bit, but it means we'd have to work with
-two-argument functions when we really only care about one.
-
-Fortunately, the `unique()` method is here to help:
-
-```java
-    Controller<Intege> volumeLevel = new Controller<>();
-    Observable<Integer> uniqueVolumeLevel = volumeLevel.unique();
-    uniqueVolumeLevel.watch(ScopeFactories.onEnter(level -> {
-        Log.d(TAG, "New volume level: " + level);
-    }));
-    volumeLevel.set(3); // Logs "New volume level: 3"
-    volumeLevel.set(3); // Does not log.
-    volumeLevel.set(3); // Does not log.
-    volumeLevel.set(4); // Logs "New volume level: 4"
-```
-
-The `unique()` method returns an `Observable` that is only activated when the
-source `Observable` gets *fresh* activation data, and ignores duplicate
-activations.
-
-By default `unique()` filters objects using the `equals()` method, but you can
-optionally supply a custom `BiPredicate<T, T>`, a function that compares two
-instances of `T` and returns `true` if, for your purposes, they should be
-considered "equal".
-
-For example, let's say we want to log *severe* volume changes, such as changes
-by more than 10 steps at a time:
-
-```java
-    Controller<Intege> volumeLevel = new Controller<>();
-    Observable<Integer> similarVolumeLevels = volumeLevel.unique(
-            (oldLevel, newLevel) -> {
-                return Math.abs(oldLevel - newLevel) < 10;
-            });
-    similarVolumeLevels.watch(ScopeFactories.onEnter(level -> {
-        Log.d(TAG, "Radically new volume level: " + level);
-    }));
-    volumeLevel.set(3); // Logs "Radically new volume level: 3"
-    volumeLevel.set(4); // Does not log.
-    volumeLevel.set(5); // Does not log.
-    volumeLevel.set(30); // Logs "Radically new volume level: 30"
-```
-
 ### Observers as Scopes
 
 Sometimes you might want to only `watch()` an `Observable` for a limited time,
diff --git a/chromecast/browser/android/apk/src/org/chromium/chromecast/shell/CastWebContentsActivity.java b/chromecast/browser/android/apk/src/org/chromium/chromecast/shell/CastWebContentsActivity.java
index c7c264ec..456345b 100644
--- a/chromecast/browser/android/apk/src/org/chromium/chromecast/shell/CastWebContentsActivity.java
+++ b/chromecast/browser/android/apk/src/org/chromium/chromecast/shell/CastWebContentsActivity.java
@@ -56,9 +56,6 @@
     private CastWebContentsSurfaceHelper mSurfaceHelper;
 
     {
-        // Create an Observable that only supplies the Intent when not finishing.
-        Observable<Intent> hasIntentState =
-                mGotIntentState.and(Observable.not(mIsFinishingState)).map(Both::getFirst);
         Observable<Intent> gotIntentAfterFinishingState =
                 mIsFinishingState.andThen(mGotIntentState).map(Both::getSecond);
         Observable<?> createdAndNotTestingState =
@@ -108,10 +105,14 @@
                 }));
 
         // Handle each new Intent.
-        hasIntentState.map(Intent::getExtras)
+        Controller<CastWebContentsSurfaceHelper.StartParams> startParamsState = new Controller<>();
+        mGotIntentState.and(Observable.not(mIsFinishingState))
+                .map(Both::getFirst)
+                .map(Intent::getExtras)
                 .map(CastWebContentsSurfaceHelper.StartParams::fromBundle)
-                .unique((previous, current) -> previous.uri.equals(current.uri))
-                .watch(ScopeFactories.onEnter(this ::notifyNewWebContents));
+                // Use the duplicate-filtering functionality of Controller to drop duplicate params.
+                .watch(ScopeFactories.onEnter(startParamsState::set));
+        startParamsState.watch(ScopeFactories.onEnter(this ::notifyNewWebContents));
 
         mIsFinishingState.watch(ScopeFactories.onEnter((String reason) -> {
             if (DEBUG) Log.d(TAG, "Finishing activity: " + reason);
diff --git a/chromecast/media/cma/backend/audio_video_pipeline_device_unittest.cc b/chromecast/media/cma/backend/audio_video_pipeline_device_unittest.cc
index 0ff0dda..f1f775f 100644
--- a/chromecast/media/cma/backend/audio_video_pipeline_device_unittest.cc
+++ b/chromecast/media/cma/backend/audio_video_pipeline_device_unittest.cc
@@ -271,7 +271,7 @@
 
   // Pause settings
   std::vector<PauseInfo> pause_pattern_;
-  int pause_pattern_idx_;
+  size_t pause_pattern_idx_;
 
   DISALLOW_COPY_AND_ASSIGN(AudioVideoPipelineDeviceTest);
 };
@@ -828,7 +828,7 @@
 #if defined(ENABLE_VIDEO_WITH_MIXED_AUDIO)
   // Do AV sync checks.
   MediaPipelineBackendForMixer* backend_for_mixer =
-      reinterpret_cast<MediaPipelineBackendForMixer*>(backend_.get());
+      static_cast<MediaPipelineBackendForMixer*>(backend_.get());
   DCHECK(backend_for_mixer);
 
   int64_t playback_start_time =
@@ -842,7 +842,7 @@
     EXPECT_TRUE(
         backend_for_mixer->video_decoder()->GetCurrentPts(&timestamp, &vpts))
         << "Getting VPTS failed at current time="
-        << " current time=" << backend_for_mixer->MonotonicClockNow()
+        << backend_for_mixer->MonotonicClockNow()
         << " playback should have started at=" << playback_start_time;
 
     // Check video started at the correct time.
@@ -856,7 +856,7 @@
   }
 #endif
 
-  if (!pause_pattern_.empty() &&
+  if (!pause_pattern_.empty() && pause_pattern_idx_ < pause_pattern_.size() &&
       pause_pattern_[pause_pattern_idx_].delay >= base::TimeDelta() &&
       media_time >= pause_time_ + pause_pattern_[pause_pattern_idx_].delay) {
     // Do Pause
@@ -893,8 +893,42 @@
   EXPECT_LT(media_time,
             pause_time_ + base::TimeDelta::FromMilliseconds(kPausePtsSlackMs));
 
+#if defined(ENABLE_VIDEO_WITH_MIXED_AUDIO)
+  // Do AV sync checks.
+  MediaPipelineBackendForMixer* backend_for_mixer =
+      static_cast<MediaPipelineBackendForMixer*>(backend_.get());
+  DCHECK(backend_for_mixer);
+
+  int64_t playback_start_time =
+      backend_for_mixer->GetPlaybackStartTimeForTesting();
+
+  if (backend_for_mixer->audio_decoder() &&
+      backend_for_mixer->video_decoder() &&
+      backend_for_mixer->MonotonicClockNow() > playback_start_time + 50000) {
+    // Check the audio time.
+    base::TimeDelta audio_time = base::TimeDelta::FromMicroseconds(
+        backend_for_mixer->audio_decoder()->GetCurrentPts());
+    EXPECT_LT(audio_time, pause_time_ + base::TimeDelta::FromMilliseconds(
+                                            kPausePtsSlackMs));
+
+    // Check the video time.
+    int64_t timestamp = 0;
+    int64_t pts = 0;
+    EXPECT_TRUE(
+        backend_for_mixer->video_decoder()->GetCurrentPts(&timestamp, &pts))
+        << "Getting VPTS failed at current time="
+        << backend_for_mixer->MonotonicClockNow()
+        << " playback should have started at=" << playback_start_time;
+
+    base::TimeDelta video_time = base::TimeDelta::FromMicroseconds(pts);
+
+    EXPECT_LT(video_time, pause_time_ + base::TimeDelta::FromMilliseconds(
+                                            kPausePtsSlackMs));
+  }
+#endif
+
   pause_time_ = media_time;
-  pause_pattern_idx_ = (pause_pattern_idx_ + 1) % pause_pattern_.size();
+  ++pause_pattern_idx_;
 
   VLOG(2) << "Pause complete, restarting media clock";
   RunPlaybackChecks();
@@ -1206,5 +1240,14 @@
   base::RunLoop().Run();
 }
 
+TEST_F(AudioVideoPipelineDeviceTest, Mp4PlaybackStartsPaused) {
+  set_sync_type(MediaPipelineDeviceParams::kModeSyncPts);
+  AddPause(base::TimeDelta(), base::TimeDelta::FromSeconds(1));
+
+  ConfigureForFile("bear.mp4");
+  Start();
+  base::RunLoop().Run();
+}
+
 }  // namespace media
 }  // namespace chromecast
diff --git a/chromeos/services/assistant/assistant_manager_service.h b/chromeos/services/assistant/assistant_manager_service.h
index bb03061..3d1bd1b 100644
--- a/chromeos/services/assistant/assistant_manager_service.h
+++ b/chromeos/services/assistant/assistant_manager_service.h
@@ -47,10 +47,6 @@
   // Returns a pointer of AssistantSettingsManager.
   virtual AssistantSettingsManager* GetAssistantSettingsManager() = 0;
 
-  // Sets assistant controller.
-  virtual void SetAssistantController(
-      ash::mojom::AssistantController* controller) = 0;
-
   using GetSettingsUiResponseCallback =
       base::OnceCallback<void(const std::string&)>;
   // Send request for getting settings ui.
diff --git a/chromeos/services/assistant/assistant_manager_service_impl.cc b/chromeos/services/assistant/assistant_manager_service_impl.cc
index 62bb327..7967549 100644
--- a/chromeos/services/assistant/assistant_manager_service_impl.cc
+++ b/chromeos/services/assistant/assistant_manager_service_impl.cc
@@ -42,17 +42,15 @@
 AssistantManagerServiceImpl::AssistantManagerServiceImpl(
     service_manager::Connector* connector,
     device::mojom::BatteryMonitorPtr battery_monitor,
-    mojom::Client* client,
-    mojom::DeviceActionsPtr device_actions)
+    Service* service)
     : platform_api_(CreateLibAssistantConfig(),
                     connector,
                     std::move(battery_monitor)),
-      device_actions_(std::move(device_actions)),
       action_module_(std::make_unique<action::CrosActionModule>(this)),
       display_connection_(std::make_unique<CrosDisplayConnection>(this)),
       main_thread_task_runner_(base::ThreadTaskRunnerHandle::Get()),
       voice_interaction_observer_binding_(this),
-      assistant_client_(client),
+      service_(service),
       background_thread_("background thread"),
       weak_factory_(this) {
   background_thread_.Start();
@@ -186,21 +184,16 @@
       base::BindOnce(
           &AssistantManagerServiceImpl::SendContextQueryAndRunCallback,
           weak_factory_.GetWeakPtr(), std::move(callback)));
-  assistant_client_->RequestAssistantStructure(
+  service_->client()->RequestAssistantStructure(
       base::BindOnce(&AssistantManagerServiceImpl::OnAssistantStructureReceived,
                      weak_factory_.GetWeakPtr(), on_done));
   // TODO(muyuanli): handle metalayer and grab only part of the screen.
-  assistant_controller_->RequestScreenshot(
+  service_->assistant_controller()->RequestScreenshot(
       region, base::BindOnce(
                   &AssistantManagerServiceImpl::OnAssistantScreenshotReceived,
                   weak_factory_.GetWeakPtr(), on_done));
 }
 
-void AssistantManagerServiceImpl::SetAssistantController(
-    ash::mojom::AssistantController* controller) {
-  assistant_controller_ = controller;
-}
-
 void AssistantManagerServiceImpl::StartVoiceInteraction() {
   assistant_manager_->StartAssistantInteraction();
 }
@@ -305,10 +298,10 @@
   if (modify_setting_args.setting_id() == kWiFiDeviceSettingId) {
     switch (modify_setting_args.change()) {
       case api::client_op::ModifySettingArgs_Change_ON:
-        device_actions_->SetWifiEnabled(true);
+        service_->device_actions()->SetWifiEnabled(true);
         return;
       case api::client_op::ModifySettingArgs_Change_OFF:
-        device_actions_->SetWifiEnabled(false);
+        service_->device_actions()->SetWifiEnabled(false);
         return;
 
       case api::client_op::ModifySettingArgs_Change_TOGGLE:
diff --git a/chromeos/services/assistant/assistant_manager_service_impl.h b/chromeos/services/assistant/assistant_manager_service_impl.h
index fe1b08d..91012f2 100644
--- a/chromeos/services/assistant/assistant_manager_service_impl.h
+++ b/chromeos/services/assistant/assistant_manager_service_impl.h
@@ -43,6 +43,8 @@
 namespace chromeos {
 namespace assistant {
 
+class Service;
+
 // Implementation of AssistantManagerService based on LibAssistant.
 // This is the main class that ineracts with LibAssistant.
 // Since LibAssistant is a standalone library, all callbacks come from it
@@ -57,10 +59,10 @@
       public ash::mojom::VoiceInteractionObserver,
       public assistant_client::DeviceStateListener {
  public:
+  // |service| owns this class and must outlive this class.
   AssistantManagerServiceImpl(service_manager::Connector* connector,
                               device::mojom::BatteryMonitorPtr battery_monitor,
-                              mojom::Client* client,
-                              mojom::DeviceActionsPtr device_actions);
+                              Service* service);
 
   ~AssistantManagerServiceImpl() override;
 
@@ -77,8 +79,6 @@
   void SendUpdateSettingsUiRequest(
       const std::string& update,
       UpdateSettingsUiResponseCallback callback) override;
-  void SetAssistantController(
-      ash::mojom::AssistantController* controller) override;
 
   // mojom::Assistant overrides:
   void StartVoiceInteraction() override;
@@ -183,7 +183,7 @@
   scoped_refptr<base::SingleThreadTaskRunner> main_thread_task_runner_;
   std::unique_ptr<assistant_client::AssistantManager> assistant_manager_;
   std::unique_ptr<AssistantSettingsManagerImpl> assistant_settings_manager_;
-  mojom::DeviceActionsPtr device_actions_;
+  // same ownership as assistant_manager_.
   assistant_client::AssistantManagerInternal* assistant_manager_internal_;
   std::unique_ptr<CrosDisplayConnection> display_connection_;
   mojo::InterfacePtrSet<mojom::AssistantEventSubscriber> subscribers_;
@@ -191,8 +191,7 @@
   mojo::Binding<ash::mojom::VoiceInteractionObserver>
       voice_interaction_observer_binding_;
 
-  ash::mojom::AssistantController* assistant_controller_;
-  mojom::Client* assistant_client_;
+  Service* service_;  // unowned.
 
   bool assistant_enabled_ = false;
   bool context_enabled_ = false;
diff --git a/chromeos/services/assistant/fake_assistant_manager_service_impl.cc b/chromeos/services/assistant/fake_assistant_manager_service_impl.cc
index dfd27ec4..6a9fff1 100644
--- a/chromeos/services/assistant/fake_assistant_manager_service_impl.cc
+++ b/chromeos/services/assistant/fake_assistant_manager_service_impl.cc
@@ -55,8 +55,5 @@
 void FakeAssistantManagerServiceImpl::AddAssistantEventSubscriber(
     mojom::AssistantEventSubscriberPtr subscriber) {}
 
-void FakeAssistantManagerServiceImpl::SetAssistantController(
-    ash::mojom::AssistantController* controller) {}
-
 }  // namespace assistant
 }  // namespace chromeos
diff --git a/chromeos/services/assistant/fake_assistant_manager_service_impl.h b/chromeos/services/assistant/fake_assistant_manager_service_impl.h
index a165a394..0b50629 100644
--- a/chromeos/services/assistant/fake_assistant_manager_service_impl.h
+++ b/chromeos/services/assistant/fake_assistant_manager_service_impl.h
@@ -36,8 +36,6 @@
   void SendUpdateSettingsUiRequest(
       const std::string& update,
       UpdateSettingsUiResponseCallback callback) override;
-  void SetAssistantController(
-      ash::mojom::AssistantController* controller) override;
 
   // mojom::Assistant overrides:
   void StartVoiceInteraction() override;
diff --git a/chromeos/services/assistant/service.cc b/chromeos/services/assistant/service.cc
index 9cb3f12..426d228 100644
--- a/chromeos/services/assistant/service.cc
+++ b/chromeos/services/assistant/service.cc
@@ -162,13 +162,13 @@
 void Service::Init(mojom::ClientPtr client,
                    mojom::DeviceActionsPtr device_actions) {
   client_ = std::move(client);
+  device_actions_ = std::move(device_actions);
 #if BUILDFLAG(ENABLE_CROS_LIBASSISTANT)
   device::mojom::BatteryMonitorPtr battery_monitor;
   context()->connector()->BindInterface(device::mojom::kServiceName,
                                         mojo::MakeRequest(&battery_monitor));
   assistant_manager_service_ = std::make_unique<AssistantManagerServiceImpl>(
-      context()->connector(), std::move(battery_monitor), client_.get(),
-      std::move(device_actions));
+      context()->connector(), std::move(battery_monitor), this);
 #else
   assistant_manager_service_ =
       std::make_unique<FakeAssistantManagerServiceImpl>();
@@ -246,8 +246,6 @@
 
   assistant_settings_manager_ =
       assistant_manager_service_.get()->GetAssistantSettingsManager();
-  assistant_manager_service_->SetAssistantController(
-      assistant_controller_.get());
   registry_.AddInterface<mojom::AssistantSettingsManager>(base::BindRepeating(
       &Service::BindAssistantSettingsManager, base::Unretained(this)));
 
diff --git a/chromeos/services/assistant/service.h b/chromeos/services/assistant/service.h
index 6aa777c..6a49183b 100644
--- a/chromeos/services/assistant/service.h
+++ b/chromeos/services/assistant/service.h
@@ -46,6 +46,12 @@
   Service();
   ~Service() override;
 
+  mojom::Client* client() { return client_.get(); }
+  mojom::DeviceActions* device_actions() { return device_actions_.get(); }
+  ash::mojom::AssistantController* assistant_controller() {
+    return assistant_controller_.get();
+  }
+
   void SetIdentityManagerForTesting(
       identity::mojom::IdentityManagerPtr identity_manager);
 
@@ -105,6 +111,7 @@
   mojo::Binding<ash::mojom::SessionActivationObserver>
       session_observer_binding_;
   mojom::ClientPtr client_;
+  mojom::DeviceActionsPtr device_actions_;
 
   identity::mojom::IdentityManagerPtr identity_manager_;
 
diff --git a/chromeos/services/device_sync/public/cpp/device_sync_client.cc b/chromeos/services/device_sync/public/cpp/device_sync_client.cc
index de88d0a..aa9a7533 100644
--- a/chromeos/services/device_sync/public/cpp/device_sync_client.cc
+++ b/chromeos/services/device_sync/public/cpp/device_sync_client.cc
@@ -20,6 +20,13 @@
   observer_list_.RemoveObserver(observer);
 }
 
+void DeviceSyncClient::NotifyReady() {
+  is_ready_ = true;
+
+  for (auto& observer : observer_list_)
+    observer.OnReady();
+}
+
 void DeviceSyncClient::NotifyEnrollmentFinished() {
   for (auto& observer : observer_list_)
     observer.OnEnrollmentFinished();
diff --git a/chromeos/services/device_sync/public/cpp/device_sync_client.h b/chromeos/services/device_sync/public/cpp/device_sync_client.h
index 4a289ea..029100a 100644
--- a/chromeos/services/device_sync/public/cpp/device_sync_client.h
+++ b/chromeos/services/device_sync/public/cpp/device_sync_client.h
@@ -24,6 +24,13 @@
  public:
   class Observer {
    public:
+    // Called when the DeviceSyncClient is ready, i.e. local device metadata
+    // and synced devices are available.
+    virtual void OnReady() {}
+
+    // OnEnrollmentFinished() and OnNewDevicesSynced() will only be called once
+    // DeviceSyncClient is ready, i.e., OnReady() will always be the first
+    // callback called.
     virtual void OnEnrollmentFinished() {}
     virtual void OnNewDevicesSynced() {}
 
@@ -42,6 +49,11 @@
   void AddObserver(Observer* observer);
   void RemoveObserver(Observer* observer);
 
+  // Clients of DeviceSyncClient should ensure that this returns true before
+  // calling any methods. If false, clients should listen on and wait for the
+  // OnReady() callback.
+  bool is_ready() { return is_ready_; }
+
   virtual void ForceEnrollmentNow(
       mojom::DeviceSync::ForceEnrollmentNowCallback callback) = 0;
   virtual void ForceSyncNow(
@@ -65,10 +77,12 @@
       mojom::DeviceSync::GetDebugInfoCallback callback) = 0;
 
  protected:
+  void NotifyReady();
   void NotifyEnrollmentFinished();
   void NotifyNewDevicesSynced();
 
  private:
+  bool is_ready_ = false;
   base::ObserverList<Observer> observer_list_;
 
   DISALLOW_COPY_AND_ASSIGN(DeviceSyncClient);
diff --git a/chromeos/services/device_sync/public/cpp/device_sync_client_impl.cc b/chromeos/services/device_sync/public/cpp/device_sync_client_impl.cc
index a9124a9..8a54fca 100644
--- a/chromeos/services/device_sync/public/cpp/device_sync_client_impl.cc
+++ b/chromeos/services/device_sync/public/cpp/device_sync_client_impl.cc
@@ -97,11 +97,13 @@
 }
 
 cryptauth::RemoteDeviceRefList DeviceSyncClientImpl::GetSyncedDevices() {
+  DCHECK(is_ready());
   return expiring_device_cache_->GetNonExpiredRemoteDevices();
 }
 
 base::Optional<cryptauth::RemoteDeviceRef>
 DeviceSyncClientImpl::GetLocalDeviceMetadata() {
+  DCHECK(is_ready());
   return local_device_id_
              ? expiring_device_cache_->GetRemoteDevice(*local_device_id_)
              : base::nullopt;
@@ -131,6 +133,25 @@
   device_sync_ptr_->GetDebugInfo(std::move(callback));
 }
 
+void DeviceSyncClientImpl::AttemptToBecomeReady() {
+  if (is_ready())
+    return;
+
+  if (waiting_for_synced_devices_ || waiting_for_local_device_metadata_)
+    return;
+
+  NotifyReady();
+
+  if (pending_notify_enrollment_finished_)
+    NotifyEnrollmentFinished();
+
+  if (pending_notify_new_synced_devices_)
+    NotifyNewDevicesSynced();
+
+  pending_notify_enrollment_finished_ = false;
+  pending_notify_new_synced_devices_ = false;
+}
+
 void DeviceSyncClientImpl::LoadSyncedDevices() {
   device_sync_ptr_->GetSyncedDevices(
       base::BindOnce(&DeviceSyncClientImpl::OnGetSyncedDevicesCompleted,
@@ -153,15 +174,22 @@
     return;
   }
 
-  if (waiting_for_local_device_metadata_) {
-    waiting_for_local_device_metadata_ = false;
+  waiting_for_synced_devices_ = false;
+
+  if (waiting_for_local_device_metadata_)
     LoadLocalDeviceMetadata();
-  }
 
   expiring_device_cache_->SetRemoteDevicesAndInvalidateOldEntries(
       *remote_devices);
 
-  NotifyNewDevicesSynced();
+  // Don't yet notify observers that new devices have synced until the client
+  // is ready.
+  if (is_ready()) {
+    NotifyNewDevicesSynced();
+  } else {
+    pending_notify_new_synced_devices_ = true;
+    AttemptToBecomeReady();
+  }
 }
 
 void DeviceSyncClientImpl::OnGetLocalDeviceMetadataCompleted(
@@ -170,14 +198,22 @@
     PA_LOG(INFO) << "Tried to get local device metadata before service was "
                     "fully initialized; waiting for enrollment to complete "
                     "before continuing.";
-    waiting_for_local_device_metadata_ = true;
     return;
   }
 
   local_device_id_ = local_device_metadata->GetDeviceId();
   expiring_device_cache_->UpdateRemoteDevice(*local_device_metadata);
 
-  NotifyEnrollmentFinished();
+  waiting_for_local_device_metadata_ = false;
+
+  // Don't yet notify observers that enrollment has finished until the client
+  // is ready.
+  if (is_ready()) {
+    NotifyEnrollmentFinished();
+  } else {
+    pending_notify_enrollment_finished_ = true;
+    AttemptToBecomeReady();
+  }
 }
 
 void DeviceSyncClientImpl::OnFindEligibleDevicesCompleted(
diff --git a/chromeos/services/device_sync/public/cpp/device_sync_client_impl.h b/chromeos/services/device_sync/public/cpp/device_sync_client_impl.h
index eb6a8a1..9daa3370 100644
--- a/chromeos/services/device_sync/public/cpp/device_sync_client_impl.h
+++ b/chromeos/services/device_sync/public/cpp/device_sync_client_impl.h
@@ -80,6 +80,8 @@
   DeviceSyncClientImpl(service_manager::Connector* connector,
                        scoped_refptr<base::TaskRunner> task_runner);
 
+  void AttemptToBecomeReady();
+
   void LoadSyncedDevices();
   void LoadLocalDeviceMetadata();
 
@@ -101,7 +103,12 @@
   mojo::Binding<mojom::DeviceSyncObserver> binding_;
   std::unique_ptr<cryptauth::ExpiringRemoteDeviceCache> expiring_device_cache_;
 
-  bool waiting_for_local_device_metadata_ = false;
+  bool waiting_for_synced_devices_ = true;
+  bool waiting_for_local_device_metadata_ = true;
+
+  bool pending_notify_enrollment_finished_ = false;
+  bool pending_notify_new_synced_devices_ = false;
+
   base::Optional<std::string> local_device_id_;
 
   base::WeakPtrFactory<DeviceSyncClientImpl> weak_ptr_factory_;
diff --git a/chromeos/services/device_sync/public/cpp/device_sync_client_impl_unittest.cc b/chromeos/services/device_sync/public/cpp/device_sync_client_impl_unittest.cc
index b9094fb..8c621ae 100644
--- a/chromeos/services/device_sync/public/cpp/device_sync_client_impl_unittest.cc
+++ b/chromeos/services/device_sync/public/cpp/device_sync_client_impl_unittest.cc
@@ -104,7 +104,20 @@
     closure_for_new_devices_synced_ = std::move(closure);
   }
 
+  void OnReady() override {
+    if (ready_count_ == 0u) {
+      // Ensure that OnReady() was called before the other callbacks.
+      EXPECT_FALSE(enrollment_finished_count_);
+      EXPECT_FALSE(new_devices_synced_count_);
+    }
+
+    ++ready_count_;
+  }
+
   void OnEnrollmentFinished() override {
+    // Ensure that OnReady() was called before the other callbacks.
+    EXPECT_TRUE(ready_count_);
+
     ++enrollment_finished_count_;
 
     EXPECT_TRUE(closure_for_enrollment_finished_);
@@ -112,16 +125,21 @@
   }
 
   void OnNewDevicesSynced() override {
+    // Ensure that OnReady() was called before the other callbacks.
+    EXPECT_TRUE(ready_count_);
+
     ++new_devices_synced_count_;
 
     EXPECT_TRUE(closure_for_new_devices_synced_);
     std::move(closure_for_new_devices_synced_).Run();
   }
 
+  size_t ready_count() { return ready_count_; }
   size_t enrollment_finished_count() { return enrollment_finished_count_; }
   size_t new_devices_synced_count() { return new_devices_synced_count_; }
 
  private:
+  size_t ready_count_ = 0u;
   size_t enrollment_finished_count_ = 0u;
   size_t new_devices_synced_count_ = 0u;
 
@@ -198,69 +216,97 @@
   }
 
   void InvokeInitialGetLocalMetadataAndThenSync() {
-    EXPECT_FALSE(client_->GetLocalDeviceMetadata());
+    EXPECT_FALSE(client_->is_ready());
+    EXPECT_EQ(0u, test_observer_->ready_count());
     EXPECT_EQ(0u, test_observer_->enrollment_finished_count());
+    EXPECT_EQ(0u, test_observer_->new_devices_synced_count());
 
-    base::RunLoop enrollment_run_loop;
-    test_observer_->set_closure_for_enrollment_finished(
-        enrollment_run_loop.QuitClosure());
     fake_device_sync_->InvokePendingGetLocalDeviceMetadataCallback(
         test_remote_device_list_[0]);
-    enrollment_run_loop.Run();
 
-    // In the case where enrollment finishes before sync, the local device
-    // metadata must still be accessible.
+    // Ensure that no Observer callbacks are called until both the local device
+    // metadata and the remote devices are supplied.
+    EXPECT_FALSE(client_->is_ready());
+    EXPECT_EQ(0u, test_observer_->ready_count());
+    EXPECT_EQ(0u, test_observer_->enrollment_finished_count());
+    EXPECT_EQ(0u, test_observer_->new_devices_synced_count());
+
+    base::RunLoop run_loop;
+
+    fake_device_sync_->InvokePendingGetSyncedDevicesCallback(
+        test_remote_device_list_);
+
+    test_observer_->set_closure_for_enrollment_finished(
+        run_loop.QuitWhenIdleClosure());
+    test_observer_->set_closure_for_new_devices_synced(
+        run_loop.QuitWhenIdleClosure());
+
+    run_loop.Run();
+
+    EXPECT_TRUE(client_->is_ready());
+    EXPECT_EQ(1u, test_observer_->ready_count());
     EXPECT_EQ(test_remote_device_list_[0].public_key,
               client_->GetLocalDeviceMetadata()->public_key());
     EXPECT_EQ(1u, test_observer_->enrollment_finished_count());
-
-    EXPECT_EQ(1u, client_->GetSyncedDevices().size());
-    EXPECT_EQ(0u, test_observer_->new_devices_synced_count());
-
-    base::RunLoop sync_run_loop;
-    test_observer_->set_closure_for_new_devices_synced(
-        sync_run_loop.QuitClosure());
-    fake_device_sync_->InvokePendingGetSyncedDevicesCallback(
-        test_remote_device_list_);
-    sync_run_loop.Run();
-
     VerifyRemoteDeviceRefListAndRemoteDeviceListAreEqual(
         client_->GetSyncedDevices(), test_remote_device_list_);
     EXPECT_EQ(1u, test_observer_->new_devices_synced_count());
   }
 
   void InvokeInitialSyncAndThenGetLocalMetadata() {
-    EXPECT_EQ(0u, client_->GetSyncedDevices().size());
+    EXPECT_FALSE(client_->is_ready());
+    EXPECT_EQ(0u, test_observer_->ready_count());
+    EXPECT_EQ(0u, test_observer_->enrollment_finished_count());
     EXPECT_EQ(0u, test_observer_->new_devices_synced_count());
 
-    base::RunLoop sync_run_loop;
-    test_observer_->set_closure_for_new_devices_synced(
-        sync_run_loop.QuitClosure());
+    // Since local device metadata has not yet been supplied at this point,
+    // |client_| will queue up another call to fetch it. The callback is handled
+    // at the end of this method.
     fake_device_sync_->InvokePendingGetSyncedDevicesCallback(
         test_remote_device_list_);
-    sync_run_loop.Run();
 
+    // Ensure that no Observer callbacks are called until both the local device
+    // metadata and the remote devices are supplied.
+    EXPECT_FALSE(client_->is_ready());
+    EXPECT_EQ(0u, test_observer_->ready_count());
+    EXPECT_EQ(0u, test_observer_->enrollment_finished_count());
+    EXPECT_EQ(0u, test_observer_->new_devices_synced_count());
+
+    base::RunLoop run_loop;
+
+    fake_device_sync_->InvokePendingGetLocalDeviceMetadataCallback(
+        test_remote_device_list_[0]);
+
+    test_observer_->set_closure_for_new_devices_synced(
+        run_loop.QuitWhenIdleClosure());
+    test_observer_->set_closure_for_enrollment_finished(
+        run_loop.QuitWhenIdleClosure());
+
+    run_loop.Run();
+
+    EXPECT_TRUE(client_->is_ready());
+    EXPECT_EQ(1u, test_observer_->ready_count());
+    EXPECT_EQ(test_remote_device_list_[0].public_key,
+              client_->GetLocalDeviceMetadata()->public_key());
+    EXPECT_EQ(1u, test_observer_->enrollment_finished_count());
     VerifyRemoteDeviceRefListAndRemoteDeviceListAreEqual(
         client_->GetSyncedDevices(), test_remote_device_list_);
     EXPECT_EQ(1u, test_observer_->new_devices_synced_count());
 
-    EXPECT_FALSE(client_->GetLocalDeviceMetadata());
-    EXPECT_EQ(0u, test_observer_->enrollment_finished_count());
+    base::RunLoop second_enrollment_run_loop;
 
-    base::RunLoop enrollment_run_loop;
-    test_observer_->set_closure_for_enrollment_finished(
-        enrollment_run_loop.QuitClosure());
     fake_device_sync_->InvokePendingGetLocalDeviceMetadataCallback(
         test_remote_device_list_[0]);
-    enrollment_run_loop.Run();
-    EXPECT_EQ(test_remote_device_list_[0].public_key,
-              client_->GetLocalDeviceMetadata()->public_key());
+    test_observer_->set_closure_for_enrollment_finished(
+        second_enrollment_run_loop.QuitWhenIdleClosure());
+
+    second_enrollment_run_loop.Run();
 
     // Ensure that the rest of the synced devices are not removed from the cache
     // when updating the local device metadata.
     VerifyRemoteDeviceRefListAndRemoteDeviceListAreEqual(
         client_->GetSyncedDevices(), test_remote_device_list_);
-    EXPECT_EQ(1u, test_observer_->enrollment_finished_count());
+    EXPECT_EQ(2u, test_observer_->enrollment_finished_count());
   }
 
   void TearDown() override {
@@ -465,43 +511,46 @@
 
   SendPendingMojoMessages();
 
-  EXPECT_FALSE(client_->GetLocalDeviceMetadata());
+  EXPECT_FALSE(client_->is_ready());
+  EXPECT_EQ(0u, test_observer_->ready_count());
   EXPECT_EQ(0u, test_observer_->enrollment_finished_count());
+  EXPECT_EQ(0u, test_observer_->new_devices_synced_count());
 
   // Simulate local device metadata not being ready. It will be ready once
   // synced devices are returned, at which point |client_| should call
   // GetLocalMetadata() again.
   fake_device_sync_->InvokePendingGetLocalDeviceMetadataCallback(base::nullopt);
-
-  EXPECT_FALSE(client_->GetLocalDeviceMetadata());
-  EXPECT_EQ(0u, test_observer_->enrollment_finished_count());
-
-  EXPECT_EQ(0u, client_->GetSyncedDevices().size());
-  EXPECT_EQ(0u, test_observer_->new_devices_synced_count());
-
-  base::RunLoop sync_run_loop;
-  test_observer_->set_closure_for_new_devices_synced(
-      sync_run_loop.QuitClosure());
   fake_device_sync_->InvokePendingGetSyncedDevicesCallback(
       test_remote_device_list_);
-  sync_run_loop.Run();
 
-  VerifyRemoteDeviceRefListAndRemoteDeviceListAreEqual(
-      client_->GetSyncedDevices(), test_remote_device_list_);
-  EXPECT_EQ(1u, test_observer_->new_devices_synced_count());
+  EXPECT_FALSE(client_->is_ready());
+  EXPECT_EQ(0u, test_observer_->ready_count());
+  EXPECT_EQ(0u, test_observer_->enrollment_finished_count());
+  EXPECT_EQ(0u, test_observer_->new_devices_synced_count());
 
   SendPendingMojoMessages();
 
-  base::RunLoop get_local_metadata_run_loop;
-  test_observer_->set_closure_for_enrollment_finished(
-      get_local_metadata_run_loop.QuitClosure());
+  base::RunLoop run_loop;
+
+  // Simulate the local device metadata now being ready.
   fake_device_sync_->InvokePendingGetLocalDeviceMetadataCallback(
       test_remote_device_list_[0]);
-  get_local_metadata_run_loop.Run();
 
+  test_observer_->set_closure_for_enrollment_finished(
+      run_loop.QuitWhenIdleClosure());
+  test_observer_->set_closure_for_new_devices_synced(
+      run_loop.QuitWhenIdleClosure());
+
+  run_loop.Run();
+
+  EXPECT_TRUE(client_->is_ready());
+  EXPECT_EQ(1u, test_observer_->ready_count());
   EXPECT_EQ(test_remote_device_list_[0].public_key,
             client_->GetLocalDeviceMetadata()->public_key());
   EXPECT_EQ(1u, test_observer_->enrollment_finished_count());
+  VerifyRemoteDeviceRefListAndRemoteDeviceListAreEqual(
+      client_->GetSyncedDevices(), test_remote_device_list_);
+  EXPECT_EQ(1u, test_observer_->new_devices_synced_count());
 }
 
 TEST_F(DeviceSyncClientImplTest, TestOnEnrollmentFinished) {
diff --git a/chromeos/services/device_sync/public/cpp/fake_device_sync_client.h b/chromeos/services/device_sync/public/cpp/fake_device_sync_client.h
index 147b467..e791573 100644
--- a/chromeos/services/device_sync/public/cpp/fake_device_sync_client.h
+++ b/chromeos/services/device_sync/public/cpp/fake_device_sync_client.h
@@ -52,6 +52,7 @@
     local_device_metadata_ = local_device_metadata;
   }
 
+  using DeviceSyncClient::NotifyReady;
   using DeviceSyncClient::NotifyEnrollmentFinished;
   using DeviceSyncClient::NotifyNewDevicesSynced;
 
diff --git a/chromeos/services/multidevice_setup/BUILD.gn b/chromeos/services/multidevice_setup/BUILD.gn
index 50bba03..d7926cc 100644
--- a/chromeos/services/multidevice_setup/BUILD.gn
+++ b/chromeos/services/multidevice_setup/BUILD.gn
@@ -29,6 +29,10 @@
     "host_verifier.h",
     "host_verifier_impl.cc",
     "host_verifier_impl.h",
+    "host_verifier_operation.cc",
+    "host_verifier_operation.h",
+    "host_verifier_operation_impl.cc",
+    "host_verifier_operation_impl.h",
     "multidevice_setup_base.cc",
     "multidevice_setup_base.h",
     "multidevice_setup_impl.cc",
@@ -47,6 +51,7 @@
     "//chromeos/components/proximity_auth/logging",
     "//chromeos/services/device_sync/public/cpp",
     "//chromeos/services/device_sync/public/mojom",
+    "//chromeos/services/multidevice_setup/proto",
     "//chromeos/services/multidevice_setup/public/mojom",
     "//chromeos/services/secure_channel/public/cpp/client",
     "//chromeos/services/secure_channel/public/mojom",
@@ -77,6 +82,8 @@
     "fake_host_status_provider.h",
     "fake_host_verifier.cc",
     "fake_host_verifier.h",
+    "fake_host_verifier_operation.cc",
+    "fake_host_verifier_operation.h",
     "fake_setup_flow_completion_recorder.cc",
     "fake_setup_flow_completion_recorder.h",
   ]
@@ -100,6 +107,7 @@
     "host_backend_delegate_impl_unittest.cc",
     "host_status_provider_impl_unittest.cc",
     "host_verifier_impl_unittest.cc",
+    "host_verifier_operation_impl_unittest.cc",
     "multidevice_setup_impl_unittest.cc",
     "multidevice_setup_service_unittest.cc",
     "setup_flow_completion_recorder_impl_unittest.cc",
diff --git a/chromeos/services/multidevice_setup/fake_host_verifier_operation.cc b/chromeos/services/multidevice_setup/fake_host_verifier_operation.cc
new file mode 100644
index 0000000..2116708d
--- /dev/null
+++ b/chromeos/services/multidevice_setup/fake_host_verifier_operation.cc
@@ -0,0 +1,32 @@
+// 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 "chromeos/services/multidevice_setup/fake_host_verifier_operation.h"
+
+namespace chromeos {
+
+namespace multidevice_setup {
+
+FakeHostVerifierOperation::FakeHostVerifierOperation(Delegate* delegate)
+    : HostVerifierOperation(delegate) {}
+
+FakeHostVerifierOperation::~FakeHostVerifierOperation() = default;
+
+void FakeHostVerifierOperation::PerformCancelOperation() {}
+
+FakeHostVerifierOperationDelegate::FakeHostVerifierOperationDelegate() =
+    default;
+
+FakeHostVerifierOperationDelegate::~FakeHostVerifierOperationDelegate() =
+    default;
+
+void FakeHostVerifierOperationDelegate::OnOperationFinished(
+    HostVerifierOperation::Result result) {
+  DCHECK(!result_);
+  result_ = result;
+}
+
+}  // namespace multidevice_setup
+
+}  // namespace chromeos
diff --git a/chromeos/services/multidevice_setup/fake_host_verifier_operation.h b/chromeos/services/multidevice_setup/fake_host_verifier_operation.h
new file mode 100644
index 0000000..af81906
--- /dev/null
+++ b/chromeos/services/multidevice_setup/fake_host_verifier_operation.h
@@ -0,0 +1,55 @@
+// 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 CHROMEOS_SERVICES_MULTIDEVICE_SETUP_FAKE_HOST_VERIFIER_OPERATION_H_
+#define CHROMEOS_SERVICES_MULTIDEVICE_SETUP_FAKE_HOST_VERIFIER_OPERATION_H_
+
+#include "base/macros.h"
+#include "base/optional.h"
+#include "chromeos/services/multidevice_setup/host_verifier_operation.h"
+
+namespace chromeos {
+
+namespace multidevice_setup {
+
+// Test HostVerifierOperation implementation.
+class FakeHostVerifierOperation : public HostVerifierOperation {
+ public:
+  FakeHostVerifierOperation(Delegate* delegate);
+  ~FakeHostVerifierOperation() override;
+
+  using HostVerifierOperation::NotifyOperationFinished;
+
+ private:
+  // HostVerifierOperation:
+  void PerformCancelOperation() override;
+
+  DISALLOW_COPY_AND_ASSIGN(FakeHostVerifierOperation);
+};
+
+// Test HostVerifierOperation::Delegate implementation.
+class FakeHostVerifierOperationDelegate
+    : public HostVerifierOperation::Delegate {
+ public:
+  FakeHostVerifierOperationDelegate();
+  ~FakeHostVerifierOperationDelegate() override;
+
+  const base::Optional<HostVerifierOperation::Result>& result() const {
+    return result_;
+  }
+
+ private:
+  // HostVerifierOperation::Delegate:
+  void OnOperationFinished(HostVerifierOperation::Result result) override;
+
+  base::Optional<HostVerifierOperation::Result> result_;
+
+  DISALLOW_COPY_AND_ASSIGN(FakeHostVerifierOperationDelegate);
+};
+
+}  // namespace multidevice_setup
+
+}  // namespace chromeos
+
+#endif  // CHROMEOS_SERVICES_MULTIDEVICE_SETUP_FAKE_HOST_VERIFIER_OPERATION_H_
diff --git a/chromeos/services/multidevice_setup/host_verifier_operation.cc b/chromeos/services/multidevice_setup/host_verifier_operation.cc
new file mode 100644
index 0000000..eaaa7b7
--- /dev/null
+++ b/chromeos/services/multidevice_setup/host_verifier_operation.cc
@@ -0,0 +1,84 @@
+// 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 "chromeos/services/multidevice_setup/host_verifier_operation.h"
+
+#include "chromeos/components/proximity_auth/logging/logging.h"
+
+namespace chromeos {
+
+namespace multidevice_setup {
+
+HostVerifierOperation::HostVerifierOperation(Delegate* delegate)
+    : delegate_(delegate) {}
+
+HostVerifierOperation::~HostVerifierOperation() = default;
+
+void HostVerifierOperation::CancelOperation() {
+  if (result_) {
+    PA_LOG(ERROR) << "HostVerifierOperation::CancelOperation(): Tried to "
+                  << "cancel operation, but it was already finished. Result: "
+                  << *result_;
+    NOTREACHED();
+  }
+
+  PerformCancelOperation();
+  NotifyOperationFinished(Result::kCanceled);
+}
+
+void HostVerifierOperation::NotifyOperationFinished(Result result) {
+  if (result_) {
+    PA_LOG(ERROR) << "HostVerifierOperation::NotifyOperationFinished(): Tried "
+                  << "to finish operation, but it was already finished. "
+                  << "Result: " << *result_;
+  }
+  result_ = result;
+
+  delegate_->OnOperationFinished(*result_);
+}
+
+std::ostream& operator<<(std::ostream& stream,
+                         const HostVerifierOperation::Result& result) {
+  switch (result) {
+    case HostVerifierOperation::Result::kTimeoutFindingEligibleDevices:
+      stream << "[timeout calling FindEligibleDevices()]";
+      break;
+    case HostVerifierOperation::Result::kErrorCallingFindEligibleDevices:
+      stream << "[error calling FindEligibleDevices()]";
+      break;
+    case HostVerifierOperation::Result::kDeviceToVerifyIsNotEligible:
+      stream << "[device to verify was not included in FindEligibleDevices() "
+             << "response];";
+      break;
+    case HostVerifierOperation::Result::kTimeoutFindingConnection:
+      stream << "[timeout finding connection]";
+      break;
+    case HostVerifierOperation::Result::kConnectionAttemptFailed:
+      stream << "[connection attempt failed]";
+      break;
+    case HostVerifierOperation::Result::kConnectionDisconnectedUnexpectedly:
+      stream << "[connection disconnected unexpectedly]";
+      break;
+    case HostVerifierOperation::Result::kTimeoutReceivingResponse:
+      stream << "[timeout receiving EnableBetterTogetherResponse]";
+      break;
+    case HostVerifierOperation::Result::kReceivedInvalidResponse:
+      stream << "[received invalid EnableBetterTogetherResponse message]";
+      break;
+    case HostVerifierOperation::Result::kReceivedErrorResponse:
+      stream << "[received EnableBetterTogetherResponse with error]";
+      break;
+    case HostVerifierOperation::Result::kCanceled:
+      stream << "[request canceled]";
+      break;
+    case HostVerifierOperation::Result::kSuccess:
+      stream << "[success]";
+      break;
+  }
+  return stream;
+}
+
+}  // namespace multidevice_setup
+
+}  // namespace chromeos
diff --git a/chromeos/services/multidevice_setup/host_verifier_operation.h b/chromeos/services/multidevice_setup/host_verifier_operation.h
new file mode 100644
index 0000000..ed0fbdf
--- /dev/null
+++ b/chromeos/services/multidevice_setup/host_verifier_operation.h
@@ -0,0 +1,79 @@
+// 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 CHROMEOS_SERVICES_MULTIDEVICE_SETUP_HOST_VERIFIER_OPERATION_H_
+#define CHROMEOS_SERVICES_MULTIDEVICE_SETUP_HOST_VERIFIER_OPERATION_H_
+
+#include <ostream>
+
+#include "base/macros.h"
+#include "base/optional.h"
+
+namespace chromeos {
+
+namespace multidevice_setup {
+
+// Operation for completing the verification step for the current host device.
+// A HostVerifierOperation instance is meant to be used for a single
+// verification attempt; if verification needs to be retried, a new instance
+// should be created for the next attempt.
+class HostVerifierOperation {
+ public:
+  enum class Result {
+    kTimeoutFindingEligibleDevices,
+    kErrorCallingFindEligibleDevices,
+    kDeviceToVerifyIsNotEligible,
+    kTimeoutFindingConnection,
+    kConnectionAttemptFailed,
+    kConnectionDisconnectedUnexpectedly,
+    kTimeoutReceivingResponse,
+    kReceivedInvalidResponse,
+    kReceivedErrorResponse,
+    kCanceled,
+    kSuccess
+  };
+
+  class Delegate {
+   public:
+    virtual ~Delegate() = default;
+    virtual void OnOperationFinished(Result result) = 0;
+  };
+
+  virtual ~HostVerifierOperation();
+
+  // Cancels the operation, triggering a delegate callback with the kCanceled
+  // result.
+  //
+  // It is invalid to call this function after the operation has already
+  // completed.
+  void CancelOperation();
+
+  // Returns the result of the operation. If the operation has not yet finished,
+  // null is returned.
+  const base::Optional<Result>& result() const { return result_; }
+
+ protected:
+  HostVerifierOperation(Delegate* delegate);
+
+  // Derived types should use this function to cancel the operation, but they
+  // should not call NotifyOperationFinished() during cancellation.
+  virtual void PerformCancelOperation() = 0;
+
+  void NotifyOperationFinished(Result result);
+
+ private:
+  Delegate* delegate_;
+  base::Optional<Result> result_;
+
+  DISALLOW_COPY_AND_ASSIGN(HostVerifierOperation);
+};
+
+std::ostream& operator<<(std::ostream& stream,
+                         const HostVerifierOperation::Result& result);
+
+}  // namespace multidevice_setup
+
+}  // namespace chromeos
+
+#endif  // CHROMEOS_SERVICES_MULTIDEVICE_SETUP_HOST_VERIFIER_OPERATION_H_
diff --git a/chromeos/services/multidevice_setup/host_verifier_operation_impl.cc b/chromeos/services/multidevice_setup/host_verifier_operation_impl.cc
new file mode 100644
index 0000000..8a71466
--- /dev/null
+++ b/chromeos/services/multidevice_setup/host_verifier_operation_impl.cc
@@ -0,0 +1,333 @@
+// 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 "chromeos/services/multidevice_setup/host_verifier_operation_impl.h"
+
+#include <sstream>
+
+#include "base/bind.h"
+#include "base/memory/ptr_util.h"
+#include "base/no_destructor.h"
+#include "base/stl_util.h"
+#include "base/time/time.h"
+#include "chromeos/components/proximity_auth/logging/logging.h"
+#include "chromeos/services/device_sync/public/cpp/device_sync_client.h"
+#include "chromeos/services/secure_channel/public/cpp/client/secure_channel_client.h"
+
+namespace chromeos {
+
+namespace multidevice_setup {
+
+namespace {
+
+const char kFeature[] = "better_together_setup";
+
+const int kNumMinutesForTimeout = 1;
+
+const char kEligibleDeviceIdsLogString[] = "Eligible device IDs";
+const char kIneligibleDeviceIdsLogString[] = "Ineligible device IDs";
+
+void LogDeviceIds(const cryptauth::RemoteDeviceRefList& device_list,
+                  const std::string& device_type_name,
+                  std::stringstream* ss) {
+  *ss << device_type_name << ": [";
+  if (!device_list.empty()) {
+    for (const auto& device : device_list)
+      *ss << "\"" << device.GetTruncatedDeviceIdForLogs() << "\", ";
+    ss->seekp(-2, ss->cur);  // Remove last ", " from the stream.
+  }
+  *ss << "]";
+}
+
+std::string CreateLogString(
+    const cryptauth::RemoteDeviceRefList& eligible_devices,
+    const cryptauth::RemoteDeviceRefList& ineligible_devices) {
+  std::stringstream ss;
+  LogDeviceIds(eligible_devices, kEligibleDeviceIdsLogString, &ss);
+  ss << ", ";
+  LogDeviceIds(ineligible_devices, kIneligibleDeviceIdsLogString, &ss);
+  return ss.str();
+}
+
+base::Optional<EnableBetterTogetherResponse> DeserializePossibleResponse(
+    const std::string& payload) {
+  BetterTogetherSetupMessageWrapper wrapper;
+
+  // If |payload| does not correspond to a BetterTogetherSetupMessageWrapper,
+  // return null.
+  if (!wrapper.ParseFromString(payload))
+    return base::nullopt;
+
+  // If |wrapper|'s type indicates that it does not contain a
+  // EnableBetterTogetherResponse, return null.
+  if (!wrapper.has_type() ||
+      wrapper.type() != MessageType::ENABLE_BETTER_TOGETHER_RESPONSE) {
+    return base::nullopt;
+  }
+
+  EnableBetterTogetherResponse response;
+
+  // If |wrapper|'s payload does not represent an EnableBetterTogetherResponse,
+  // return null.
+  if (!wrapper.has_payload() || !response.ParseFromString(wrapper.payload()))
+    return base::nullopt;
+
+  return response;
+}
+
+}  // namespace
+
+// static
+HostVerifierOperationImpl::Factory*
+    HostVerifierOperationImpl::Factory::test_factory_ = nullptr;
+
+// static
+HostVerifierOperationImpl::Factory* HostVerifierOperationImpl::Factory::Get() {
+  if (test_factory_)
+    return test_factory_;
+
+  static base::NoDestructor<Factory> factory;
+  return factory.get();
+}
+
+// static
+void HostVerifierOperationImpl::Factory::SetFactoryForTesting(
+    Factory* test_factory) {
+  test_factory_ = test_factory;
+}
+
+HostVerifierOperationImpl::Factory::~Factory() = default;
+
+std::unique_ptr<HostVerifierOperation>
+HostVerifierOperationImpl::Factory::BuildInstance(
+    HostVerifierOperation::Delegate* delegate,
+    cryptauth::RemoteDeviceRef device_to_connect,
+    cryptauth::RemoteDeviceRef local_device,
+    device_sync::DeviceSyncClient* device_sync_client,
+    secure_channel::SecureChannelClient* secure_channel_client,
+    std::unique_ptr<base::OneShotTimer> timer) {
+  return base::WrapUnique(new HostVerifierOperationImpl(
+      delegate, device_to_connect, local_device, device_sync_client,
+      secure_channel_client, std::move(timer)));
+}
+
+// static
+BetterTogetherSetupMessageWrapper
+HostVerifierOperationImpl::CreateWrappedEnableBetterTogetherRequest() {
+  BetterTogetherSetupMessageWrapper wrapper;
+  wrapper.set_type(MessageType::ENABLE_BETTER_TOGETHER_REQUEST);
+
+  EnableBetterTogetherRequest request;
+  wrapper.set_payload(request.SerializeAsString());
+
+  return wrapper;
+}
+
+HostVerifierOperationImpl::HostVerifierOperationImpl(
+    HostVerifierOperation::Delegate* delegate,
+    cryptauth::RemoteDeviceRef device_to_connect,
+    cryptauth::RemoteDeviceRef local_device,
+    device_sync::DeviceSyncClient* device_sync_client,
+    secure_channel::SecureChannelClient* secure_channel_client,
+    std::unique_ptr<base::OneShotTimer> timer)
+    : HostVerifierOperation(delegate),
+      device_to_connect_(device_to_connect),
+      local_device_(local_device),
+      device_sync_client_(device_sync_client),
+      secure_channel_client_(secure_channel_client),
+      timer_(std::move(timer)),
+      weak_ptr_factory_(this) {
+  timer_->Start(FROM_HERE, base::TimeDelta::FromMinutes(kNumMinutesForTimeout),
+                base::Bind(&HostVerifierOperationImpl::OnTimeout,
+                           base::Unretained(this)));
+  device_sync_client_->FindEligibleDevices(
+      cryptauth::SoftwareFeature::BETTER_TOGETHER_HOST,
+      base::BindOnce(&HostVerifierOperationImpl::OnFindEligibleDevicesResponse,
+                     weak_ptr_factory_.GetWeakPtr()));
+}
+
+HostVerifierOperationImpl::~HostVerifierOperationImpl() = default;
+
+void HostVerifierOperationImpl::PerformCancelOperation() {
+  FinishOperation(status_, Result::kCanceled);
+}
+
+void HostVerifierOperationImpl::OnConnectionAttemptFailure(
+    secure_channel::mojom::ConnectionAttemptFailureReason reason) {
+  PA_LOG(WARNING) << "HostVerifierOperationImpl::OnConnectionAttemptFailure(): "
+                  << "Failed to establish connection to device with ID \""
+                  << device_to_connect_.GetTruncatedDeviceIdForLogs() << "\". "
+                  << "Reason: " << reason;
+  FinishOperation(Status::kWaitingForConnection,
+                  Result::kConnectionAttemptFailed);
+}
+
+void HostVerifierOperationImpl::OnConnection(
+    std::unique_ptr<secure_channel::ClientChannel> channel) {
+  client_channel_ = std::move(channel);
+  client_channel_->AddObserver(this);
+
+  TransitionStatus(Status::kWaitingForConnection, Status::kWaitingForResponse);
+  client_channel_->SendMessage(
+      CreateWrappedEnableBetterTogetherRequest().SerializeAsString(),
+      base::DoNothing() /* on_sent_callback */);
+}
+
+void HostVerifierOperationImpl::OnDisconnected() {
+  // Disconnections may occur after the operation is finished.
+  if (status_ == Status::kFinished)
+    return;
+
+  PA_LOG(WARNING) << "HostVerifierOperationImpl::OnDisconnected(): "
+                  << "Channel disconnected unexpectedly; could not complete "
+                  << "verification of device with ID \""
+                  << device_to_connect_.GetTruncatedDeviceIdForLogs() << "\". ";
+  FinishOperation(Status::kWaitingForResponse,
+                  Result::kConnectionDisconnectedUnexpectedly);
+}
+
+void HostVerifierOperationImpl::OnMessageReceived(const std::string& payload) {
+  base::Optional<EnableBetterTogetherResponse> possible_response =
+      DeserializePossibleResponse(payload);
+
+  // The message could have been unrelated; continue waiting.
+  if (!possible_response)
+    return;
+
+  // If the received message is malformed, fail the operation.
+  if (!possible_response->has_result_code() ||
+      !EnableBetterTogetherResponse::ResultCode_IsValid(
+          possible_response->result_code())) {
+    FinishOperation(Status::kWaitingForResponse,
+                    Result::kReceivedInvalidResponse);
+    return;
+  }
+
+  // If the received message includes an error, fail the operation.
+  if (possible_response->result_code() == EnableBetterTogetherResponse::ERROR) {
+    FinishOperation(Status::kWaitingForResponse,
+                    Result::kReceivedErrorResponse);
+    return;
+  }
+
+  FinishOperation(Status::kWaitingForResponse, Result::kSuccess);
+}
+
+void HostVerifierOperationImpl::OnTimeout() {
+  switch (status_) {
+    case Status::kWaitingForFindEligibleDevicesResponse:
+      FinishOperation(status_, Result::kTimeoutFindingEligibleDevices);
+      break;
+    case Status::kWaitingForConnection:
+      FinishOperation(status_, Result::kTimeoutFindingConnection);
+      break;
+    case Status::kWaitingForResponse:
+      FinishOperation(status_, Result::kTimeoutReceivingResponse);
+      break;
+    case Status::kFinished:
+      PA_LOG(ERROR) << "HostVerifierOperationImpl::OnTimeout(): Timeout "
+                    << "occurred, but the operation had already finished.";
+      NOTREACHED();
+      break;
+  }
+}
+
+void HostVerifierOperationImpl::OnFindEligibleDevicesResponse(
+    const base::Optional<std::string>& error_code,
+    cryptauth::RemoteDeviceRefList eligible_devices,
+    cryptauth::RemoteDeviceRefList ineligible_devices) {
+  // A response may be received after the operation is finished.
+  if (status_ == Status::kFinished)
+    return;
+
+  if (error_code) {
+    PA_LOG(WARNING) << "HostVerifierOperationImpl::"
+                    << "OnFindEligibleDevicesResponse(): Failed to complete "
+                    << "FindEligibleDevices() call. Error code: "
+                    << *error_code;
+    FinishOperation(Status::kWaitingForFindEligibleDevicesResponse,
+                    Result::kErrorCallingFindEligibleDevices);
+    return;
+  }
+
+  PA_LOG(INFO) << "HostVerifierOperationImpl::OnFindEligibleDevicesResponse(): "
+               << "Received FindEligibleDevices() response. "
+               << CreateLogString(eligible_devices, ineligible_devices);
+
+  if (!base::ContainsValue(eligible_devices, device_to_connect_)) {
+    PA_LOG(WARNING) << "HostVerifierOperationImpl::"
+                    << "OnFindEligibleDevicesResponse(): FindEligibleDevices() "
+                    << "response does not include the device to connect. ID: "
+                    << device_to_connect_.GetTruncatedDeviceIdForLogs();
+    FinishOperation(Status::kWaitingForFindEligibleDevicesResponse,
+                    Result::kDeviceToVerifyIsNotEligible);
+    return;
+  }
+
+  TransitionStatus(Status::kWaitingForFindEligibleDevicesResponse,
+                   Status::kWaitingForConnection);
+
+  connection_attempt_ = secure_channel_client_->ListenForConnectionFromDevice(
+      device_to_connect_, local_device_, kFeature,
+      secure_channel::ConnectionPriority::kHigh);
+  connection_attempt_->SetDelegate(this);
+}
+
+void HostVerifierOperationImpl::FinishOperation(Status expected_current_status,
+                                                Result result) {
+  TransitionStatus(expected_current_status, Status::kFinished);
+
+  if (client_channel_) {
+    client_channel_->RemoveObserver(this);
+    client_channel_.reset();
+  }
+
+  connection_attempt_.reset();
+  timer_->Stop();
+
+  if (result == Result::kCanceled)
+    return;
+
+  NotifyOperationFinished(result);
+}
+
+void HostVerifierOperationImpl::TransitionStatus(Status expected_current_status,
+                                                 Status new_status) {
+  if (status_ != expected_current_status) {
+    PA_LOG(ERROR) << "HostVerifierOperationImpl::VerifyCurrentStatus(): "
+                  << "Current status is unexpected. Current: " << status_
+                  << ", Expected: " << expected_current_status
+                  << ", Attempted new status: " << new_status;
+    NOTREACHED();
+  }
+
+  PA_LOG(INFO) << "HostVerifierOperationImpl::TransitionStatus(): "
+               << "Transitioning from " << status_ << " to " << new_status
+               << ".";
+  status_ = new_status;
+}
+
+std::ostream& operator<<(std::ostream& stream,
+                         const HostVerifierOperationImpl::Status& status) {
+  switch (status) {
+    case HostVerifierOperationImpl::Status::
+        kWaitingForFindEligibleDevicesResponse:
+      stream << "[waiting for FindEligibleDevices() response]";
+      break;
+    case HostVerifierOperationImpl::Status::kWaitingForConnection:
+      stream << "[waiting for connection]";
+      break;
+    case HostVerifierOperationImpl::Status::kWaitingForResponse:
+      stream << "[waiting for response]";
+      break;
+    case HostVerifierOperationImpl::Status::kFinished:
+      stream << "[finished]";
+      break;
+  }
+  return stream;
+}
+
+}  // namespace multidevice_setup
+
+}  // namespace chromeos
diff --git a/chromeos/services/multidevice_setup/host_verifier_operation_impl.h b/chromeos/services/multidevice_setup/host_verifier_operation_impl.h
new file mode 100644
index 0000000..5fadde8
--- /dev/null
+++ b/chromeos/services/multidevice_setup/host_verifier_operation_impl.h
@@ -0,0 +1,133 @@
+// 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 CHROMEOS_SERVICES_MULTIDEVICE_SETUP_HOST_VERIFIER_OPERATION_IMPL_H_
+#define CHROMEOS_SERVICES_MULTIDEVICE_SETUP_HOST_VERIFIER_OPERATION_IMPL_H_
+
+#include <memory>
+#include <ostream>
+#include <string>
+
+#include "base/macros.h"
+#include "base/memory/weak_ptr.h"
+#include "base/optional.h"
+#include "base/timer/timer.h"
+#include "chromeos/services/multidevice_setup/host_verifier_operation.h"
+#include "chromeos/services/multidevice_setup/proto/multidevice_setup.pb.h"
+#include "chromeos/services/secure_channel/public/cpp/client/client_channel.h"
+#include "chromeos/services/secure_channel/public/cpp/client/connection_attempt.h"
+#include "chromeos/services/secure_channel/public/mojom/secure_channel.mojom.h"
+#include "components/cryptauth/remote_device_ref.h"
+
+namespace chromeos {
+
+namespace device_sync {
+class DeviceSyncClient;
+}  // namespace device_sync
+
+namespace secure_channel {
+class SecureChannelClient;
+}  // namespace secure_channel
+
+namespace multidevice_setup {
+
+// Concrete HostVerifierOperation implementation. To verify the host, this class
+// performs the following steps:
+// (1) Call FindEligibleDevices(). This step sends a message to the host device,
+//     which in turn enables background advertising.
+// (2) Creates a connection to the device using the BLE listener role.
+// (3) Sends an EnableBetterTogetherRequest message to the host device.
+// (4) Waits for an EnableBetterTogetherResponse messages to be returned by the
+//     host device.
+class HostVerifierOperationImpl
+    : public HostVerifierOperation,
+      public secure_channel::ConnectionAttempt::Delegate,
+      public secure_channel::ClientChannel::Observer {
+ public:
+  class Factory {
+   public:
+    static Factory* Get();
+    static void SetFactoryForTesting(Factory* test_factory);
+    virtual ~Factory();
+    virtual std::unique_ptr<HostVerifierOperation> BuildInstance(
+        HostVerifierOperation::Delegate* delegate,
+        cryptauth::RemoteDeviceRef device_to_connect,
+        cryptauth::RemoteDeviceRef local_device,
+        device_sync::DeviceSyncClient* device_sync_client,
+        secure_channel::SecureChannelClient* secure_channel_client,
+        std::unique_ptr<base::OneShotTimer> timer =
+            std::make_unique<base::OneShotTimer>());
+
+   private:
+    static Factory* test_factory_;
+  };
+
+  ~HostVerifierOperationImpl() override;
+
+ private:
+  friend class MultiDeviceSetupHostVerifierOperationImplTest;
+
+  enum class Status {
+    kWaitingForFindEligibleDevicesResponse,
+    kWaitingForConnection,
+    kWaitingForResponse,
+    kFinished
+  };
+  friend std::ostream& operator<<(std::ostream& stream, const Status& status);
+
+  HostVerifierOperationImpl(
+      HostVerifierOperation::Delegate* delegate,
+      cryptauth::RemoteDeviceRef device_to_connect,
+      cryptauth::RemoteDeviceRef local_device,
+      device_sync::DeviceSyncClient* device_sync_client,
+      secure_channel::SecureChannelClient* secure_channel_client,
+      std::unique_ptr<base::OneShotTimer> timer);
+
+  static BetterTogetherSetupMessageWrapper
+  CreateWrappedEnableBetterTogetherRequest();
+
+  // HostVerifierOperation:
+  void PerformCancelOperation() override;
+
+  // secure_channel::ConnectionAttempt::Delegate:
+  void OnConnectionAttemptFailure(
+      secure_channel::mojom::ConnectionAttemptFailureReason reason) override;
+  void OnConnection(
+      std::unique_ptr<secure_channel::ClientChannel> channel) override;
+
+  // secure_channel::ClientChannel::Observer:
+  void OnDisconnected() override;
+  void OnMessageReceived(const std::string& payload) override;
+
+  void OnTimeout();
+  void OnFindEligibleDevicesResponse(
+      const base::Optional<std::string>& error_code,
+      cryptauth::RemoteDeviceRefList eligible_devices,
+      cryptauth::RemoteDeviceRefList ineligible_devices);
+  void FinishOperation(Status expected_current_status, Result result);
+  void TransitionStatus(Status expected_current_status, Status new_status);
+
+  cryptauth::RemoteDeviceRef device_to_connect_;
+  cryptauth::RemoteDeviceRef local_device_;
+  device_sync::DeviceSyncClient* device_sync_client_;
+  secure_channel::SecureChannelClient* secure_channel_client_;
+  std::unique_ptr<base::OneShotTimer> timer_;
+
+  Status status_ = Status::kWaitingForFindEligibleDevicesResponse;
+  std::unique_ptr<secure_channel::ConnectionAttempt> connection_attempt_;
+  std::unique_ptr<secure_channel::ClientChannel> client_channel_;
+
+  base::WeakPtrFactory<HostVerifierOperationImpl> weak_ptr_factory_;
+
+  DISALLOW_COPY_AND_ASSIGN(HostVerifierOperationImpl);
+};
+
+std::ostream& operator<<(std::ostream& stream,
+                         const HostVerifierOperationImpl::Status& status);
+
+}  // namespace multidevice_setup
+
+}  // namespace chromeos
+
+#endif  // CHROMEOS_SERVICES_MULTIDEVICE_SETUP_HOST_VERIFIER_OPERATION_IMPL_H_
diff --git a/chromeos/services/multidevice_setup/host_verifier_operation_impl_unittest.cc b/chromeos/services/multidevice_setup/host_verifier_operation_impl_unittest.cc
new file mode 100644
index 0000000..864f92e
--- /dev/null
+++ b/chromeos/services/multidevice_setup/host_verifier_operation_impl_unittest.cc
@@ -0,0 +1,344 @@
+// 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 "chromeos/services/multidevice_setup/host_verifier_operation_impl.h"
+
+#include <memory>
+
+#include "base/macros.h"
+#include "base/timer/mock_timer.h"
+#include "chromeos/services/device_sync/public/cpp/fake_device_sync_client.h"
+#include "chromeos/services/multidevice_setup/fake_host_verifier_operation.h"
+#include "chromeos/services/secure_channel/public/cpp/client/fake_client_channel.h"
+#include "chromeos/services/secure_channel/public/cpp/client/fake_connection_attempt.h"
+#include "chromeos/services/secure_channel/public/cpp/client/fake_secure_channel_client.h"
+#include "components/cryptauth/remote_device_test_util.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace chromeos {
+
+namespace multidevice_setup {
+
+namespace {
+
+const size_t kNumTestDevices = 5;
+
+enum class ResponseType {
+  kUnrelated,
+  kNoResultCode,
+  kInvalidResultCode,
+  kErrorResultCode,
+  kSuccess
+};
+
+std::string CreatePayloadForResponseType(ResponseType response_type) {
+  BetterTogetherSetupMessageWrapper wrapper;
+  wrapper.set_type(MessageType::ENABLE_BETTER_TOGETHER_RESPONSE);
+
+  switch (response_type) {
+    case ResponseType::kUnrelated: {
+      return "unrelated";
+    }
+
+    case ResponseType::kNoResultCode: {
+      EnableBetterTogetherResponse response;
+      wrapper.set_payload(response.SerializeAsString());
+      return wrapper.SerializeAsString();
+    }
+
+    case ResponseType::kInvalidResultCode: {
+      EnableBetterTogetherResponse response;
+      response.set_result_code(
+          static_cast<EnableBetterTogetherResponse_ResultCode>(1337));
+      wrapper.set_payload(response.SerializeAsString());
+      return wrapper.SerializeAsString();
+    }
+
+    case ResponseType::kErrorResultCode: {
+      EnableBetterTogetherResponse response;
+      response.set_result_code(EnableBetterTogetherResponse::ERROR);
+      wrapper.set_payload(response.SerializeAsString());
+      return wrapper.SerializeAsString();
+    }
+
+    case ResponseType::kSuccess: {
+      EnableBetterTogetherResponse response;
+      response.set_result_code(EnableBetterTogetherResponse::NORMAL);
+      wrapper.set_payload(response.SerializeAsString());
+      return wrapper.SerializeAsString();
+    }
+  }
+}
+
+}  // namespace
+
+class MultiDeviceSetupHostVerifierOperationImplTest : public testing::Test {
+ protected:
+  MultiDeviceSetupHostVerifierOperationImplTest()
+      : test_devices_(
+            cryptauth::CreateRemoteDeviceRefListForTest(kNumTestDevices)) {}
+  ~MultiDeviceSetupHostVerifierOperationImplTest() override = default;
+
+  // testing::Test:
+  void SetUp() override {
+    fake_delegate_ = std::make_unique<FakeHostVerifierOperationDelegate>();
+    fake_device_sync_client_ =
+        std::make_unique<device_sync::FakeDeviceSyncClient>();
+    fake_secure_channel_client_ =
+        std::make_unique<secure_channel::FakeSecureChannelClient>();
+    auto mock_timer = std::make_unique<base::MockOneShotTimer>();
+    mock_timer_ = mock_timer.get();
+
+    operation_ = HostVerifierOperationImpl::Factory::Get()->BuildInstance(
+        fake_delegate_.get(), remote_device(), local_device(),
+        fake_device_sync_client_.get(), fake_secure_channel_client_.get(),
+        std::move(mock_timer));
+
+    // The operation should have started its timer immediately.
+    EXPECT_TRUE(mock_timer_->IsRunning());
+  }
+
+  void Timeout() { mock_timer_->Fire(); }
+
+  void CancelAndVerifyResult() {
+    operation_->CancelOperation();
+    EXPECT_EQ(HostVerifierOperation::Result::kCanceled, GetOperationResult());
+  }
+
+  // Note: If |error_code| is set, then |should_remote_device_be_eligible| is
+  // ignored. The eligible/ineligible device lists are only provided if there
+  // was no error.
+  void CompletePendingFindEligibleDevicesResponse(
+      const base::Optional<std::string>& error_code = base::nullopt,
+      bool should_remote_device_be_eligible = true) {
+    cryptauth::RemoteDeviceRefList eligible_devices;
+    cryptauth::RemoteDeviceRefList ineligible_devices;
+
+    if (!error_code) {
+      // Always make device 2 eligible.
+      eligible_devices.push_back(test_devices_[2]);
+
+      if (should_remote_device_be_eligible)
+        eligible_devices.push_back(remote_device());
+      else
+        ineligible_devices.push_back(remote_device());
+
+      // Always make the local device as well as devices 3 and 4 ineligible.
+      ineligible_devices.push_back(local_device());
+      ineligible_devices.push_back(test_devices_[3]);
+      ineligible_devices.push_back(test_devices_[4]);
+
+      if (should_remote_device_be_eligible) {
+        auto fake_connection_attempt =
+            std::make_unique<secure_channel::FakeConnectionAttempt>();
+        fake_connection_attempt_ = fake_connection_attempt.get();
+        fake_secure_channel_client_->set_next_listen_connection_attempt(
+            remote_device(), local_device(),
+            std::move(fake_connection_attempt));
+      }
+    }
+
+    EXPECT_FALSE(GetOperationResult());
+    fake_device_sync_client_->InvokePendingFindEligibleDevicesCallback(
+        error_code, eligible_devices, ineligible_devices);
+
+    if (error_code) {
+      EXPECT_EQ(HostVerifierOperation::Result::kErrorCallingFindEligibleDevices,
+                GetOperationResult());
+    } else if (!should_remote_device_be_eligible) {
+      EXPECT_EQ(HostVerifierOperation::Result::kDeviceToVerifyIsNotEligible,
+                GetOperationResult());
+    } else {
+      EXPECT_FALSE(GetOperationResult());
+    }
+  }
+
+  void FailToCreateConnectionAndVerifyState(
+      secure_channel::mojom::ConnectionAttemptFailureReason failure_reason) {
+    EXPECT_FALSE(GetOperationResult());
+    fake_connection_attempt_->NotifyConnectionAttemptFailure(failure_reason);
+    EXPECT_EQ(HostVerifierOperation::Result::kConnectionAttemptFailed,
+              GetOperationResult());
+  }
+
+  void CreateConnectionSuccessfully() {
+    EXPECT_FALSE(GetOperationResult());
+    auto fake_client_channel =
+        std::make_unique<secure_channel::FakeClientChannel>();
+    fake_client_channel_ = fake_client_channel.get();
+    fake_connection_attempt_->NotifyConnection(std::move(fake_client_channel));
+
+    EXPECT_EQ(1u, fake_client_channel_->sent_messages().size());
+    EXPECT_EQ(
+        HostVerifierOperationImpl::CreateWrappedEnableBetterTogetherRequest()
+            .SerializeAsString(),
+        fake_client_channel_->sent_messages()[0].first);
+  }
+
+  void ReceiveResponseAndVerifyState(ResponseType response_type) {
+    fake_client_channel_->NotifyMessageReceived(
+        CreatePayloadForResponseType(response_type));
+
+    switch (response_type) {
+      case ResponseType::kUnrelated:
+        EXPECT_FALSE(GetOperationResult());
+        break;
+      case ResponseType::kNoResultCode:
+        EXPECT_EQ(HostVerifierOperation::Result::kReceivedInvalidResponse,
+                  GetOperationResult());
+        break;
+      case ResponseType::kInvalidResultCode:
+        EXPECT_EQ(HostVerifierOperation::Result::kReceivedInvalidResponse,
+                  GetOperationResult());
+        break;
+      case ResponseType::kErrorResultCode:
+        EXPECT_EQ(HostVerifierOperation::Result::kReceivedErrorResponse,
+                  GetOperationResult());
+        break;
+      case ResponseType::kSuccess:
+        EXPECT_EQ(HostVerifierOperation::Result::kSuccess,
+                  GetOperationResult());
+        break;
+    }
+  }
+
+  base::Optional<HostVerifierOperation::Result> GetOperationResult() {
+    // Both |operation_| and |fake_delegate_| should have identical result
+    // values.
+    EXPECT_EQ(operation_->result(), fake_delegate_->result());
+    return operation_->result();
+  }
+
+  secure_channel::FakeClientChannel* fake_client_channel() {
+    return fake_client_channel_;
+  }
+
+  const cryptauth::RemoteDeviceRef& local_device() { return test_devices_[0]; }
+  const cryptauth::RemoteDeviceRef& remote_device() { return test_devices_[1]; }
+
+ private:
+  const cryptauth::RemoteDeviceRefList test_devices_;
+
+  std::unique_ptr<FakeHostVerifierOperationDelegate> fake_delegate_;
+  std::unique_ptr<device_sync::FakeDeviceSyncClient> fake_device_sync_client_;
+  std::unique_ptr<secure_channel::FakeSecureChannelClient>
+      fake_secure_channel_client_;
+  base::MockOneShotTimer* mock_timer_ = nullptr;
+
+  secure_channel::FakeConnectionAttempt* fake_connection_attempt_ = nullptr;
+  secure_channel::FakeClientChannel* fake_client_channel_ = nullptr;
+
+  std::unique_ptr<HostVerifierOperation> operation_;
+
+  DISALLOW_COPY_AND_ASSIGN(MultiDeviceSetupHostVerifierOperationImplTest);
+};
+
+TEST_F(MultiDeviceSetupHostVerifierOperationImplTest,
+       TimeoutFindingEligibleDevices) {
+  Timeout();
+  EXPECT_EQ(HostVerifierOperation::Result::kTimeoutFindingEligibleDevices,
+            GetOperationResult());
+}
+
+TEST_F(MultiDeviceSetupHostVerifierOperationImplTest,
+       CancelWhileFindingEligibleDevices) {
+  CancelAndVerifyResult();
+}
+
+TEST_F(MultiDeviceSetupHostVerifierOperationImplTest,
+       ErrorCallingFindEligibleDevices) {
+  CompletePendingFindEligibleDevicesResponse("errorCode");
+}
+
+TEST_F(MultiDeviceSetupHostVerifierOperationImplTest,
+       DeviceToVerifyIsNotEligible) {
+  CompletePendingFindEligibleDevicesResponse(
+      base::nullopt /* error_code */,
+      false /* should_remote_device_be_eligible */);
+}
+
+TEST_F(MultiDeviceSetupHostVerifierOperationImplTest,
+       TimeoutFindingConnection) {
+  CompletePendingFindEligibleDevicesResponse();
+  Timeout();
+  EXPECT_EQ(HostVerifierOperation::Result::kTimeoutFindingConnection,
+            GetOperationResult());
+}
+
+TEST_F(MultiDeviceSetupHostVerifierOperationImplTest,
+       CancelWhileFindingConnection) {
+  CompletePendingFindEligibleDevicesResponse();
+  CancelAndVerifyResult();
+}
+
+TEST_F(MultiDeviceSetupHostVerifierOperationImplTest, ConnectionAttemptFailed) {
+  CompletePendingFindEligibleDevicesResponse();
+  FailToCreateConnectionAndVerifyState(
+      secure_channel::mojom::ConnectionAttemptFailureReason::
+          AUTHENTICATION_ERROR);
+}
+
+TEST_F(MultiDeviceSetupHostVerifierOperationImplTest,
+       ConnectionDisconnectedUnexpectedly) {
+  CompletePendingFindEligibleDevicesResponse();
+  CreateConnectionSuccessfully();
+  fake_client_channel()->NotifyDisconnected();
+  EXPECT_EQ(HostVerifierOperation::Result::kConnectionDisconnectedUnexpectedly,
+            GetOperationResult());
+}
+
+TEST_F(MultiDeviceSetupHostVerifierOperationImplTest,
+       TimeoutReceivingResponse) {
+  CompletePendingFindEligibleDevicesResponse();
+  CreateConnectionSuccessfully();
+  Timeout();
+  EXPECT_EQ(HostVerifierOperation::Result::kTimeoutReceivingResponse,
+            GetOperationResult());
+}
+
+TEST_F(MultiDeviceSetupHostVerifierOperationImplTest,
+       CancelWhileWaitingForResponse) {
+  CompletePendingFindEligibleDevicesResponse();
+  CreateConnectionSuccessfully();
+  CancelAndVerifyResult();
+}
+
+TEST_F(MultiDeviceSetupHostVerifierOperationImplTest,
+       ReceivedInvalidResponse_NoResultCode) {
+  CompletePendingFindEligibleDevicesResponse();
+  CreateConnectionSuccessfully();
+  ReceiveResponseAndVerifyState(ResponseType::kNoResultCode);
+}
+
+TEST_F(MultiDeviceSetupHostVerifierOperationImplTest,
+       ReceivedInvalidResponse_InvalidResultCode) {
+  CompletePendingFindEligibleDevicesResponse();
+  CreateConnectionSuccessfully();
+  ReceiveResponseAndVerifyState(ResponseType::kInvalidResultCode);
+}
+
+TEST_F(MultiDeviceSetupHostVerifierOperationImplTest,
+       ReceivedInvalidResponse_ErrorResultCode) {
+  CompletePendingFindEligibleDevicesResponse();
+  CreateConnectionSuccessfully();
+  ReceiveResponseAndVerifyState(ResponseType::kErrorResultCode);
+}
+
+TEST_F(MultiDeviceSetupHostVerifierOperationImplTest, Success) {
+  CompletePendingFindEligibleDevicesResponse();
+  CreateConnectionSuccessfully();
+  ReceiveResponseAndVerifyState(ResponseType::kSuccess);
+}
+
+TEST_F(MultiDeviceSetupHostVerifierOperationImplTest,
+       ReceiveUnrelatedMessageThenSuccess) {
+  CompletePendingFindEligibleDevicesResponse();
+  CreateConnectionSuccessfully();
+  ReceiveResponseAndVerifyState(ResponseType::kUnrelated);
+  ReceiveResponseAndVerifyState(ResponseType::kSuccess);
+}
+
+}  // namespace multidevice_setup
+
+}  // namespace chromeos
diff --git a/chromeos/services/multidevice_setup/multidevice_setup_initializer.cc b/chromeos/services/multidevice_setup/multidevice_setup_initializer.cc
index 0639d951..bf95473 100644
--- a/chromeos/services/multidevice_setup/multidevice_setup_initializer.cc
+++ b/chromeos/services/multidevice_setup/multidevice_setup_initializer.cc
@@ -52,7 +52,7 @@
     : pref_service_(pref_service),
       device_sync_client_(device_sync_client),
       secure_channel_client_(secure_channel_client) {
-  if (device_sync_client_->GetLocalDeviceMetadata()) {
+  if (device_sync_client_->is_ready()) {
     InitializeImplementation();
     return;
   }
@@ -90,10 +90,7 @@
   std::move(callback).Run(false /* success */);
 }
 
-void MultiDeviceSetupInitializer::OnEnrollmentFinished() {
-  if (!device_sync_client_->GetLocalDeviceMetadata())
-    return;
-
+void MultiDeviceSetupInitializer::OnReady() {
   device_sync_client_->RemoveObserver(this);
   InitializeImplementation();
 }
diff --git a/chromeos/services/multidevice_setup/multidevice_setup_initializer.h b/chromeos/services/multidevice_setup/multidevice_setup_initializer.h
index 36a5d41a..a8a72fc0 100644
--- a/chromeos/services/multidevice_setup/multidevice_setup_initializer.h
+++ b/chromeos/services/multidevice_setup/multidevice_setup_initializer.h
@@ -57,7 +57,7 @@
       TriggerEventForDebuggingCallback callback) override;
 
   // device_sync::DeviceSyncClient::Observer:
-  void OnEnrollmentFinished() override;
+  void OnReady() override;
 
   void InitializeImplementation();
 
diff --git a/chromeos/services/multidevice_setup/multidevice_setup_service_unittest.cc b/chromeos/services/multidevice_setup/multidevice_setup_service_unittest.cc
index 6f4aa23..71506c8e 100644
--- a/chromeos/services/multidevice_setup/multidevice_setup_service_unittest.cc
+++ b/chromeos/services/multidevice_setup/multidevice_setup_service_unittest.cc
@@ -160,7 +160,7 @@
   void FinishInitialization() {
     EXPECT_FALSE(fake_multidevice_setup());
     fake_device_sync_client_->set_local_device_metadata(test_device_);
-    fake_device_sync_client_->NotifyEnrollmentFinished();
+    fake_device_sync_client_->NotifyReady();
     EXPECT_TRUE(fake_multidevice_setup());
   }
 
diff --git a/chromeos/services/multidevice_setup/proto/BUILD.gn b/chromeos/services/multidevice_setup/proto/BUILD.gn
new file mode 100644
index 0000000..7453653
--- /dev/null
+++ b/chromeos/services/multidevice_setup/proto/BUILD.gn
@@ -0,0 +1,11 @@
+# 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("//third_party/protobuf/proto_library.gni")
+
+proto_library("proto") {
+  sources = [
+    "multidevice_setup.proto",
+  ]
+}
diff --git a/chromeos/services/multidevice_setup/proto/multidevice_setup.proto b/chromeos/services/multidevice_setup/proto/multidevice_setup.proto
new file mode 100644
index 0000000..6b1efda
--- /dev/null
+++ b/chromeos/services/multidevice_setup/proto/multidevice_setup.proto
@@ -0,0 +1,38 @@
+// 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.
+
+syntax = "proto2";
+
+package chromeos.multidevice_setup;
+
+option optimize_for = LITE_RUNTIME;
+
+enum MessageType {
+  UNKNOWN_TYPE = 0;
+  ENABLE_BETTER_TOGETHER_REQUEST = 1;
+  ENABLE_BETTER_TOGETHER_RESPONSE = 2;
+}
+
+// Client to host, indicating that the client is requesting Better Together
+// setup.
+message EnableBetterTogetherRequest {}
+
+// Host to client, indicating that Better Together setup was completed.
+// Next id: 2
+message EnableBetterTogetherResponse {
+  enum ResultCode {
+    NORMAL = 0;
+    ERROR = 1;
+  }
+
+  optional ResultCode result_code = 1;
+}
+
+// Wrapper that Better Together setup messages use to explicitly indicate
+// message type.
+// Next id: 3
+message BetterTogetherSetupMessageWrapper {
+  required MessageType type = 1;
+  optional bytes payload = 2;
+}
diff --git a/components/arc/arc_util.cc b/components/arc/arc_util.cc
index 54d45ac3..6acaeb3e8 100644
--- a/components/arc/arc_util.cc
+++ b/components/arc/arc_util.cc
@@ -83,7 +83,7 @@
 }
 
 bool IsPlayStoreAvailable() {
-  if (IsRobotAccountMode())
+  if (IsRobotOrOfflineDemoAccountMode())
     return false;
   const auto* command_line = base::CommandLine::ForCurrentProcess();
   if (!command_line->HasSwitch(chromeos::switches::kArcStartMode))
@@ -143,7 +143,7 @@
          user_manager::UserManager::Get()->IsLoggedInAsArcKioskApp();
 }
 
-bool IsRobotAccountMode() {
+bool IsRobotOrOfflineDemoAccountMode() {
   return user_manager::UserManager::IsInitialized() &&
          (user_manager::UserManager::Get()->IsLoggedInAsArcKioskApp() ||
           user_manager::UserManager::Get()->IsLoggedInAsPublicAccount());
diff --git a/components/arc/arc_util.h b/components/arc/arc_util.h
index a674319..3a6c323e 100644
--- a/components/arc/arc_util.h
+++ b/components/arc/arc_util.h
@@ -78,12 +78,15 @@
 // should also return true in that case.
 bool IsArcKioskMode();
 
-// Returns true if current user is a robot account user.
-// These are Public Session and ARC Kiosk users.
+// Returns true if current user is a robot account user, or offline demo mode
+// user.
+// These are Public Session and ARC Kiosk users. Note that demo mode, including
+// offline demo mode, is implemented as a Public Session - offline demo mode
+// is setup offline and it isn't associated with a working robot account.
 // As it can return true only when user is already initialized, it implies
 // that ARC availability was checked before.
-// The check is basically IsArcKioskMode() | IsPublicSessionMode().
-bool IsRobotAccountMode();
+// The check is basically IsArcKioskMode() | IsLoggedInAsPublicSession().
+bool IsRobotOrOfflineDemoAccountMode();
 
 // Returns true if ARC is allowed for the given user. Note this should not be
 // used as a signal of whether ARC is allowed alone because it only considers
diff --git a/components/arc/common/auth.mojom b/components/arc/common/auth.mojom
index 5cf12bd..4498975 100644
--- a/components/arc/common/auth.mojom
+++ b/components/arc/common/auth.mojom
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-// Next MinVersion: 14
+// Next MinVersion: 15
 
 module arc.mojom;
 
@@ -128,7 +128,7 @@
 // These values describe the type of the Chrome account to provision.
 [Extensible]
 enum ChromeAccountType {
-  // Next value: 5
+  // Next value: 6
   UNKNOWN = 0,
 
   // Chrome login account type is a user account.
@@ -142,6 +142,11 @@
 
   // Chrome login account type is a child account.
   CHILD_ACCOUNT = 4,
+
+  // Chrome login account is a demo session account that was enrolled offline,
+  // using policies bundled with Chrome OS, and thus does not have a working
+  // robot account associated with it.
+  [MinVersion=14] OFFLINE_DEMO_ACCOUNT = 5,
 };
 
 // These values describe the type of the metrics to report.
diff --git a/components/autofill/content/browser/content_autofill_driver.cc b/components/autofill/content/browser/content_autofill_driver.cc
index 10784fd..418e62b8 100644
--- a/components/autofill/content/browser/content_autofill_driver.cc
+++ b/components/autofill/content/browser/content_autofill_driver.cc
@@ -80,6 +80,13 @@
           GetURLRequestContext();
 }
 
+scoped_refptr<network::SharedURLLoaderFactory>
+ContentAutofillDriver::GetURLLoaderFactory() {
+  return content::BrowserContext::GetDefaultStoragePartition(
+             render_frame_host_->GetSiteInstance()->GetBrowserContext())
+      ->GetURLLoaderFactoryForBrowserProcess();
+}
+
 bool ContentAutofillDriver::RendererIsAvailable() {
   return render_frame_host_->GetRenderViewHost() != nullptr;
 }
diff --git a/components/autofill/content/browser/content_autofill_driver.h b/components/autofill/content/browser/content_autofill_driver.h
index 96d93f94..0187577 100644
--- a/components/autofill/content/browser/content_autofill_driver.h
+++ b/components/autofill/content/browser/content_autofill_driver.h
@@ -51,6 +51,7 @@
   // AutofillDriver:
   bool IsIncognito() const override;
   net::URLRequestContextGetter* GetURLRequestContext() override;
+  scoped_refptr<network::SharedURLLoaderFactory> GetURLLoaderFactory() override;
   bool RendererIsAvailable() override;
   void SendFormDataToRenderer(int query_id,
                               RendererFormDataAction action,
diff --git a/components/autofill/core/browser/BUILD.gn b/components/autofill/core/browser/BUILD.gn
index c1370aff..55ae4594 100644
--- a/components/autofill/core/browser/BUILD.gn
+++ b/components/autofill/core/browser/BUILD.gn
@@ -340,6 +340,8 @@
     "//components/ukm:test_support",
     "//google_apis:test_support",
     "//services/identity/public/cpp:test_support",
+    "//services/network:test_support",
+    "//services/network/public/cpp",
     "//skia",
     "//testing/gtest",
     "//third_party/libaddressinput:util",
diff --git a/components/autofill/core/browser/DEPS b/components/autofill/core/browser/DEPS
index b569636..5c07ea7 100644
--- a/components/autofill/core/browser/DEPS
+++ b/components/autofill/core/browser/DEPS
@@ -24,6 +24,7 @@
   "+services/identity/public",
   "+services/metrics/public",
   "+services/network/public",
+  "+services/network/test",
   "+sql",
   "+third_party/fips181",
   "+third_party/libaddressinput", # For address i18n.
diff --git a/components/autofill/core/browser/autofill_driver.h b/components/autofill/core/browser/autofill_driver.h
index 2ed290d..5e71f95 100644
--- a/components/autofill/core/browser/autofill_driver.h
+++ b/components/autofill/core/browser/autofill_driver.h
@@ -7,12 +7,17 @@
 
 #include <vector>
 
+#include "base/memory/scoped_refptr.h"
 #include "components/autofill/core/common/form_data.h"
 
 namespace net {
 class URLRequestContextGetter;
 }
 
+namespace network {
+class SharedURLLoaderFactory;
+}
+
 namespace gfx {
 class RectF;
 }
@@ -42,6 +47,10 @@
   // Returns the URL request context information associated with this driver.
   virtual net::URLRequestContextGetter* GetURLRequestContext() = 0;
 
+  // Returns the URL loader factory associated with this driver.
+  virtual scoped_refptr<network::SharedURLLoaderFactory>
+  GetURLLoaderFactory() = 0;
+
   // Returns true iff the renderer is available for communication.
   virtual bool RendererIsAvailable() = 0;
 
diff --git a/components/autofill/core/browser/autofill_experiments.cc b/components/autofill/core/browser/autofill_experiments.cc
index b684380e1b..9f194616 100644
--- a/components/autofill/core/browser/autofill_experiments.cc
+++ b/components/autofill/core/browser/autofill_experiments.cc
@@ -71,7 +71,7 @@
     "AutofillUpstreamSendPanFirstSix", base::FEATURE_DISABLED_BY_DEFAULT};
 const base::Feature kAutofillUpstreamUpdatePromptExplanation{
     "AutofillUpstreamUpdatePromptExplanation",
-    base::FEATURE_DISABLED_BY_DEFAULT};
+    base::FEATURE_ENABLED_BY_DEFAULT};
 const base::Feature kAutofillVoteUsingInvalidProfileData{
     "AutofillVoteUsingInvalidProfileData", base::FEATURE_ENABLED_BY_DEFAULT};
 
diff --git a/components/autofill/core/browser/autofill_manager.cc b/components/autofill/core/browser/autofill_manager.cc
index c978873..734231e 100644
--- a/components/autofill/core/browser/autofill_manager.cc
+++ b/components/autofill/core/browser/autofill_manager.cc
@@ -69,6 +69,7 @@
 #include "components/prefs/pref_service.h"
 #include "components/security_state/core/security_state.h"
 #include "components/strings/grit/components_strings.h"
+#include "services/network/public/cpp/shared_url_loader_factory.h"
 #include "ui/base/l10n/l10n_util.h"
 #include "ui/gfx/geometry/rect.h"
 #include "url/gurl.h"
@@ -1150,7 +1151,7 @@
     : AutofillHandler(driver),
       client_(client),
       payments_client_(std::make_unique<payments::PaymentsClient>(
-          driver->GetURLRequestContext(),
+          driver->GetURLLoaderFactory(),
           client->GetPrefs(),
           client->GetIdentityManager(),
           /*unmask_delegate=*/this,
diff --git a/components/autofill/core/browser/autofill_manager.h b/components/autofill/core/browser/autofill_manager.h
index f22b0d9..8a8df3a 100644
--- a/components/autofill/core/browser/autofill_manager.h
+++ b/components/autofill/core/browser/autofill_manager.h
@@ -275,6 +275,9 @@
   }
 
   // Exposed for testing.
+  payments::PaymentsClient* payments_client() { return payments_client_.get(); }
+
+  // Exposed for testing.
   void set_payments_client(payments::PaymentsClient* payments_client) {
     payments_client_.reset(payments_client);
   }
diff --git a/components/autofill/core/browser/credit_card_save_manager_unittest.cc b/components/autofill/core/browser/credit_card_save_manager_unittest.cc
index 9bf2f1a..d7bae49 100644
--- a/components/autofill/core/browser/credit_card_save_manager_unittest.cc
+++ b/components/autofill/core/browser/credit_card_save_manager_unittest.cc
@@ -47,6 +47,7 @@
 #include "net/url_request/url_request_context_getter.h"
 #include "net/url_request/url_request_test_util.h"
 #include "services/metrics/public/cpp/ukm_builders.h"
+#include "services/network/public/cpp/shared_url_loader_factory.h"
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "url/gurl.h"
@@ -100,7 +101,7 @@
         base::ThreadTaskRunnerHandle::Get());
     autofill_driver_->SetURLRequestContext(request_context_.get());
     payments_client_ = new payments::TestPaymentsClient(
-        autofill_driver_->GetURLRequestContext(), autofill_client_.GetPrefs(),
+        autofill_driver_->GetURLLoaderFactory(), autofill_client_.GetPrefs(),
         autofill_client_.GetIdentityManager(),
         /*unmask_delegate=*/nullptr,
         // Will be set by CreditCardSaveManager's ctor
@@ -139,6 +140,11 @@
         kAutofillUpstreamUpdatePromptExplanation);
   }
 
+  void DisableAutofillUpstreamUpdatePromptExplanationExperiment() {
+    scoped_feature_list_.InitAndDisableFeature(
+        kAutofillUpstreamUpdatePromptExplanation);
+  }
+
   void FormsSeen(const std::vector<FormData>& forms) {
     autofill_manager_->OnFormsSeen(forms, base::TimeTicks());
   }
@@ -462,7 +468,9 @@
   EXPECT_CALL(autofill_client_, ConfirmSaveCreditCardLocally(_, _)).Times(0);
   FormSubmitted(credit_card_form);
   EXPECT_TRUE(credit_card_save_manager_->CreditCardWasUploaded());
-  EXPECT_TRUE(payments_client_->GetActiveExperimentsSetInRequest().empty());
+  EXPECT_THAT(
+      payments_client_->GetActiveExperimentsSetInRequest(),
+      UnorderedElementsAre(kAutofillUpstreamUpdatePromptExplanation.name));
 
   // Server did not send a server_id, expect copy of card is not stored.
   EXPECT_TRUE(personal_data_.GetCreditCards().empty());
@@ -517,7 +525,9 @@
   EXPECT_CALL(autofill_client_, ConfirmSaveCreditCardLocally(_, _)).Times(0);
   FormSubmitted(credit_card_form);
   EXPECT_TRUE(credit_card_save_manager_->CreditCardWasUploaded());
-  EXPECT_TRUE(payments_client_->GetActiveExperimentsSetInRequest().empty());
+  EXPECT_THAT(
+      payments_client_->GetActiveExperimentsSetInRequest(),
+      UnorderedElementsAre(kAutofillUpstreamUpdatePromptExplanation.name));
 
   // Server did not send a server_id, expect copy of card is not stored.
   EXPECT_TRUE(personal_data_.GetCreditCards().empty());
@@ -601,7 +611,9 @@
   EXPECT_CALL(autofill_client_, ConfirmSaveCreditCardLocally(_, _)).Times(0);
   FormSubmitted(credit_card_form);
   EXPECT_TRUE(credit_card_save_manager_->CreditCardWasUploaded());
-  EXPECT_TRUE(payments_client_->GetActiveExperimentsSetInRequest().empty());
+  EXPECT_THAT(
+      payments_client_->GetActiveExperimentsSetInRequest(),
+      UnorderedElementsAre(kAutofillUpstreamUpdatePromptExplanation.name));
 
   // Server did not send a server_id, expect copy of card is not stored.
   EXPECT_TRUE(personal_data_.GetCreditCards().empty());
@@ -3267,7 +3279,7 @@
   credit_card_form.fields[3].value = ASCIIToUTF16(NextYear());
   credit_card_form.fields[4].value = ASCIIToUTF16("123");
 
-  // Confirm that upload happened and that the enabled UpdatePromptExplanation
+  // Confirm upload happened and that the enabled UpdatePromptExplanation
   // experiment flag state was sent in the request.
   EXPECT_CALL(autofill_client_, ConfirmSaveCreditCardLocally(_, _)).Times(0);
   FormSubmitted(credit_card_form);
@@ -3279,6 +3291,7 @@
 
 TEST_F(CreditCardSaveManagerTest,
        UploadCreditCard_DoNotAddAnyFlagStatesToRequestIfExperimentsOff) {
+  DisableAutofillUpstreamUpdatePromptExplanationExperiment();
   personal_data_.ClearProfiles();
   credit_card_save_manager_->SetCreditCardUploadEnabled(true);
 
@@ -3302,8 +3315,8 @@
   credit_card_form.fields[3].value = ASCIIToUTF16(NextYear());
   credit_card_form.fields[4].value = ASCIIToUTF16("123");
 
-  // Confirm upload happened and that no experiment flag state was sent in the
-  // request.
+  // Confirm that upload happened and that no experiment flag state was sent in
+  // the request.
   EXPECT_CALL(autofill_client_, ConfirmSaveCreditCardLocally(_, _)).Times(0);
   FormSubmitted(credit_card_form);
   EXPECT_TRUE(credit_card_save_manager_->CreditCardWasUploaded());
@@ -3341,10 +3354,12 @@
   FormSubmitted(credit_card_form);
   EXPECT_TRUE(credit_card_save_manager_->CreditCardWasUploaded());
   EXPECT_EQ(payments_client_->GetPanFirstSixSetInRequest(), "444433");
-  // Confirm that the "send pan first six" experiment flag was sent in the
-  // request.
-  EXPECT_THAT(payments_client_->GetActiveExperimentsSetInRequest(),
-              UnorderedElementsAre(kAutofillUpstreamSendPanFirstSix.name));
+  // Confirm that the "send pan first six" experiment flag and enabled
+  // UpdatePromptExplanation experiment flag state was sent in the request.
+  EXPECT_THAT(
+      payments_client_->GetActiveExperimentsSetInRequest(),
+      UnorderedElementsAre(kAutofillUpstreamSendPanFirstSix.name,
+                           kAutofillUpstreamUpdatePromptExplanation.name));
 }
 
 TEST_F(CreditCardSaveManagerTest, UploadCreditCard_UploadOfLocalCard) {
diff --git a/components/autofill/core/browser/payments/full_card_request_unittest.cc b/components/autofill/core/browser/payments/full_card_request_unittest.cc
index 3f95ca3a..03d1b3e 100644
--- a/components/autofill/core/browser/payments/full_card_request_unittest.cc
+++ b/components/autofill/core/browser/payments/full_card_request_unittest.cc
@@ -22,6 +22,9 @@
 #include "components/prefs/pref_service.h"
 #include "components/prefs/testing_pref_service.h"
 #include "net/url_request/url_request_test_util.h"
+#include "services/network/public/cpp/shared_url_loader_factory.h"
+#include "services/network/public/cpp/weak_wrapper_shared_url_loader_factory.h"
+#include "services/network/test/test_url_loader_factory.h"
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
@@ -68,14 +71,17 @@
  public:
   FullCardRequestTest()
       : request_context_(new net::TestURLRequestContextGetter(
-            base::ThreadTaskRunnerHandle::Get())) {
+            base::ThreadTaskRunnerHandle::Get())),
+        test_shared_loader_factory_(
+            base::MakeRefCounted<network::WeakWrapperSharedURLLoaderFactory>(
+                &test_url_loader_factory_)) {
     std::unique_ptr<TestingPrefServiceSimple> pref_service(
         new TestingPrefServiceSimple());
     pref_service->registry()->RegisterDoublePref(
         prefs::kAutofillBillingCustomerNumber, 0.0);
     autofill_client_.SetPrefs(std::move(pref_service));
     payments_client_ = std::make_unique<PaymentsClient>(
-        request_context_.get(), autofill_client_.GetPrefs(),
+        test_shared_loader_factory_, autofill_client_.GetPrefs(),
         autofill_client_.GetIdentityManager(), this, nullptr);
     request_ = std::make_unique<FullCardRequest>(
         &autofill_client_, payments_client_.get(), &personal_data_);
@@ -112,6 +118,8 @@
   MockUIDelegate ui_delegate_;
   TestAutofillClient autofill_client_;
   scoped_refptr<net::TestURLRequestContextGetter> request_context_;
+  network::TestURLLoaderFactory test_url_loader_factory_;
+  scoped_refptr<network::SharedURLLoaderFactory> test_shared_loader_factory_;
   std::unique_ptr<PaymentsClient> payments_client_;
   std::unique_ptr<FullCardRequest> request_;
 
diff --git a/components/autofill/core/browser/payments/payments_client.cc b/components/autofill/core/browser/payments/payments_client.cc
index 9a17602..8d2b7874 100644
--- a/components/autofill/core/browser/payments/payments_client.cc
+++ b/components/autofill/core/browser/payments/payments_client.cc
@@ -30,9 +30,10 @@
 #include "net/base/load_flags.h"
 #include "net/http/http_status_code.h"
 #include "net/traffic_annotation/network_traffic_annotation.h"
-#include "net/url_request/url_fetcher.h"
-#include "net/url_request/url_request_context_getter.h"
 #include "services/identity/public/cpp/identity_manager.h"
+#include "services/network/public/cpp/resource_request.h"
+#include "services/network/public/cpp/shared_url_loader_factory.h"
+#include "services/network/public/cpp/simple_url_loader.h"
 
 namespace autofill {
 namespace payments {
@@ -453,13 +454,14 @@
     const UploadRequestDetails& other) = default;
 PaymentsClient::UploadRequestDetails::~UploadRequestDetails() {}
 
-PaymentsClient::PaymentsClient(net::URLRequestContextGetter* context_getter,
-                               PrefService* pref_service,
-                               identity::IdentityManager* identity_manager,
-                               PaymentsClientUnmaskDelegate* unmask_delegate,
-                               PaymentsClientSaveDelegate* save_delegate,
-                               bool is_off_the_record)
-    : context_getter_(context_getter),
+PaymentsClient::PaymentsClient(
+    scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory,
+    PrefService* pref_service,
+    identity::IdentityManager* identity_manager,
+    PaymentsClientUnmaskDelegate* unmask_delegate,
+    PaymentsClientSaveDelegate* save_delegate,
+    bool is_off_the_record)
+    : url_loader_factory_(url_loader_factory),
       pref_service_(pref_service),
       identity_manager_(identity_manager),
       unmask_delegate_(unmask_delegate),
@@ -517,98 +519,64 @@
                                   bool authenticate) {
   request_ = std::move(request);
   has_retried_authorization_ = false;
-  InitializeUrlFetcher();
 
-  if (!authenticate)
-    url_fetcher_->Start();
-  else if (access_token_.empty())
+  InitializeResourceRequest();
+
+  if (!authenticate) {
+    StartRequest();
+  } else if (access_token_.empty()) {
     StartTokenFetch(false);
-  else
+  } else {
     SetOAuth2TokenAndStartRequest();
+  }
 }
 
-void PaymentsClient::InitializeUrlFetcher() {
-  net::NetworkTrafficAnnotationTag traffic_annotation =
-      net::DefineNetworkTrafficAnnotation("payments_sync_cards", R"(
-        semantics {
-          sender: "Payments"
-          description:
-            "This service communicates with Google Payments servers to upload "
-            "(save) or receive the user's credit card info."
-          trigger:
-            "Requests are triggered by a user action, such as selecting a "
-            "masked server card from Chromium's credit card autofill dropdown, "
-            "submitting a form which has credit card information, or accepting "
-            "the prompt to save a credit card to Payments servers."
-          data:
-            "In case of save, a protocol buffer containing relevant address "
-            "and credit card information which should be saved in Google "
-            "Payments servers, along with user credentials. In case of load, a "
-            "protocol buffer containing the id of the credit card to unmask, "
-            "an encrypted cvc value, an optional updated card expiration date, "
-            "and user credentials."
-          destination: GOOGLE_OWNED_SERVICE
-        }
-        policy {
-          cookies_allowed: NO
-          setting:
-            "Users can enable or disable this feature in Chromium settings by "
-            "toggling 'Credit cards and addresses using Google Payments', "
-            "under 'Advanced sync settings...'. This feature is enabled by "
-            "default."
-          chrome_policy {
-            AutoFillEnabled {
-              policy_options {mode: MANDATORY}
-              AutoFillEnabled: false
-            }
-          }
-        })");
-  url_fetcher_ =
-      net::URLFetcher::Create(0, GetRequestUrl(request_->GetRequestUrlPath()),
-                              net::URLFetcher::POST, this, traffic_annotation);
-
-  data_use_measurement::DataUseUserData::AttachToFetcher(
-      url_fetcher_.get(), data_use_measurement::DataUseUserData::AUTOFILL);
-  url_fetcher_->SetRequestContext(context_getter_.get());
-  url_fetcher_->SetLoadFlags(net::LOAD_DO_NOT_SAVE_COOKIES |
-                             net::LOAD_DO_NOT_SEND_COOKIES |
-                             net::LOAD_DISABLE_CACHE);
-
-  url_fetcher_->SetUploadData(request_->GetRequestContentType(),
-                              request_->GetRequestContent());
-
+void PaymentsClient::InitializeResourceRequest() {
+  resource_request_ = std::make_unique<network::ResourceRequest>();
+  resource_request_->url = GetRequestUrl(request_->GetRequestUrlPath());
+  resource_request_->load_flags = net::LOAD_DO_NOT_SAVE_COOKIES |
+                                  net::LOAD_DO_NOT_SEND_COOKIES |
+                                  net::LOAD_DISABLE_CACHE;
+  resource_request_->method = "POST";
   if (base::FeatureList::IsEnabled(
           features::kAutofillSendExperimentIdsInPaymentsRPCs)) {
     // Add Chrome experiment state to the request headers.
     net::HttpRequestHeaders headers;
     // User is always signed-in to be able to upload card to Google Payments.
-    variations::AppendVariationHeaders(url_fetcher_->GetOriginalURL(),
-                                       is_off_the_record_
-                                           ? variations::InIncognito::kYes
-                                           : variations::InIncognito::kNo,
-                                       variations::SignedIn::kYes, &headers);
-    url_fetcher_->SetExtraRequestHeaders(headers.ToString());
+    variations::AppendVariationHeaders(
+        resource_request_->url,
+        is_off_the_record_ ? variations::InIncognito::kYes
+                           : variations::InIncognito::kNo,
+        variations::SignedIn::kYes, &resource_request_->headers);
   }
 }
 
 void PaymentsClient::CancelRequest() {
   request_.reset();
-  url_fetcher_.reset();
+  resource_request_.reset();
+  simple_url_loader_.reset();
   token_fetcher_.reset();
   access_token_.clear();
   has_retried_authorization_ = false;
 }
 
-void PaymentsClient::OnURLFetchComplete(const net::URLFetcher* source) {
-  DCHECK_EQ(source, url_fetcher_.get());
-
-  // |url_fetcher_|, which is aliased to |source|, might continue to be used in
-  // this method, but should be freed once control leaves the method.
-  std::unique_ptr<net::URLFetcher> scoped_url_fetcher(std::move(url_fetcher_));
-  std::unique_ptr<base::DictionaryValue> response_dict;
-  int response_code = source->GetResponseCode();
+void PaymentsClient::OnSimpleLoaderComplete(
+    std::unique_ptr<std::string> response_body) {
+  int response_code = -1;
+  if (simple_url_loader_->ResponseInfo() &&
+      simple_url_loader_->ResponseInfo()->headers) {
+    response_code =
+        simple_url_loader_->ResponseInfo()->headers->response_code();
+  }
   std::string data;
-  source->GetResponseAsString(&data);
+  if (response_body)
+    data = std::move(*response_body);
+  OnSimpleLoaderCompleteInternal(response_code, data);
+}
+
+void PaymentsClient::OnSimpleLoaderCompleteInternal(int response_code,
+                                                    const std::string& data) {
+  std::unique_ptr<base::DictionaryValue> response_dict;
   VLOG(2) << "Got data: " << data;
 
   AutofillClient::PaymentsRpcResult result = AutofillClient::SUCCESS;
@@ -640,7 +608,7 @@
       }
       has_retried_authorization_ = true;
 
-      InitializeUrlFetcher();
+      InitializeResourceRequest();
       StartTokenFetch(true);
       return;
     }
@@ -678,16 +646,16 @@
   }
 
   access_token_ = access_token;
-  if (url_fetcher_)
+  if (resource_request_)
     SetOAuth2TokenAndStartRequest();
 }
 
 void PaymentsClient::AccessTokenError(const GoogleServiceAuthError& error) {
   VLOG(1) << "Unhandled OAuth2 error: " << error.ToString();
-  if (url_fetcher_) {
-    url_fetcher_.reset();
+  if (simple_url_loader_)
+    simple_url_loader_.reset();
+  if (request_)
     request_->RespondToDelegate(AutofillClient::PERMANENT_FAILURE);
-  }
 }
 
 void PaymentsClient::StartTokenFetch(bool invalidate_old) {
@@ -712,10 +680,67 @@
 }
 
 void PaymentsClient::SetOAuth2TokenAndStartRequest() {
-  url_fetcher_->AddExtraRequestHeader(net::HttpRequestHeaders::kAuthorization +
-                                      std::string(": Bearer ") + access_token_);
+  DCHECK(resource_request_);
+  resource_request_->headers.AddHeaderFromString(
+      net::HttpRequestHeaders::kAuthorization + std::string(": Bearer ") +
+      access_token_);
+  StartRequest();
+}
 
-  url_fetcher_->Start();
+void PaymentsClient::StartRequest() {
+  DCHECK(resource_request_);
+  net::NetworkTrafficAnnotationTag traffic_annotation =
+      net::DefineNetworkTrafficAnnotation("payments_sync_cards", R"(
+        semantics {
+          sender: "Payments"
+          description:
+            "This service communicates with Google Payments servers to upload "
+            "(save) or receive the user's credit card info."
+          trigger:
+            "Requests are triggered by a user action, such as selecting a "
+            "masked server card from Chromium's credit card autofill dropdown, "
+            "submitting a form which has credit card information, or accepting "
+            "the prompt to save a credit card to Payments servers."
+          data:
+            "In case of save, a protocol buffer containing relevant address "
+            "and credit card information which should be saved in Google "
+            "Payments servers, along with user credentials. In case of load, a "
+            "protocol buffer containing the id of the credit card to unmask, "
+            "an encrypted cvc value, an optional updated card expiration date, "
+            "and user credentials."
+          destination: GOOGLE_OWNED_SERVICE
+        }
+        policy {
+          cookies_allowed: NO
+          setting:
+            "Users can enable or disable this feature in Chromium settings by "
+            "toggling 'Credit cards and addresses using Google Payments', "
+            "under 'Advanced sync settings...'. This feature is enabled by "
+            "default."
+          chrome_policy {
+            AutoFillEnabled {
+              policy_options {mode: MANDATORY}
+              AutoFillEnabled: false
+            }
+          }
+        })");
+  // TODO(https://crbug.com/808498): Re-add data use measurement once
+  // SimpleURLLoader supports it.
+  // ID=data_use_measurement::DataUseUserData::AUTOFILL
+  simple_url_loader_ = network::SimpleURLLoader::Create(
+      std::move(resource_request_), traffic_annotation);
+  simple_url_loader_->AttachStringForUpload(request_->GetRequestContent(),
+                                            request_->GetRequestContentType());
+
+  simple_url_loader_->DownloadToStringOfUnboundedSizeUntilCrashAndDie(
+      url_loader_factory_.get(),
+      base::BindOnce(&PaymentsClient::OnSimpleLoaderComplete,
+                     base::Unretained(this)));
+}
+
+void PaymentsClient::set_url_loader_factory_for_testing(
+    scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory) {
+  url_loader_factory_ = std::move(url_loader_factory);
 }
 
 }  // namespace payments
diff --git a/components/autofill/core/browser/payments/payments_client.h b/components/autofill/core/browser/payments/payments_client.h
index 5d6f274d..6595a72e 100644
--- a/components/autofill/core/browser/payments/payments_client.h
+++ b/components/autofill/core/browser/payments/payments_client.h
@@ -7,6 +7,7 @@
 
 #include "base/macros.h"
 #include "base/memory/ref_counted.h"
+#include "base/memory/scoped_refptr.h"
 #include "base/memory/weak_ptr.h"
 #include "components/autofill/core/browser/autofill_client.h"
 #include "components/autofill/core/browser/autofill_profile.h"
@@ -14,17 +15,17 @@
 #include "components/autofill/core/browser/credit_card.h"
 #include "components/prefs/pref_service.h"
 #include "google_apis/gaia/google_service_auth_error.h"
-#include "net/url_request/url_fetcher_delegate.h"
 
 namespace identity {
 class IdentityManager;
 class PrimaryAccountAccessTokenFetcher;
 }  // namespace identity
 
-namespace net {
-class URLFetcher;
-class URLRequestContextGetter;
-}
+namespace network {
+struct ResourceRequest;
+class SimpleURLLoader;
+class SharedURLLoaderFactory;
+}  // namespace network
 
 namespace autofill {
 
@@ -62,7 +63,7 @@
 // request will cancel a pending request.
 // Tests are located in
 // src/components/autofill/content/browser/payments/payments_client_unittest.cc.
-class PaymentsClient : public net::URLFetcherDelegate {
+class PaymentsClient {
  public:
   // The names of the fields used to send non-location elements as part of an
   // address. Used in the implementation and in tests which verify that these
@@ -100,19 +101,20 @@
     std::vector<const char*> active_experiments;
   };
 
-  // |context_getter| is reference counted so it has no lifetime or ownership
-  // requirements. |pref_service| is used to get the registered preference
-  // value, |identity_manager|, |unmask_delegate| and |save_delegate| must all
-  // outlive |this|. Either delegate might be nullptr. |is_off_the_record|
-  // denotes incognito mode.
-  PaymentsClient(net::URLRequestContextGetter* context_getter,
-                 PrefService* pref_service,
-                 identity::IdentityManager* identity_manager,
-                 PaymentsClientUnmaskDelegate* unmask_delegate,
-                 PaymentsClientSaveDelegate* save_delegate,
-                 bool is_off_the_record = false);
+  // |url_loader_factory| is reference counted so it has no lifetime or
+  // ownership requirements. |pref_service| is used to get the registered
+  // preference value, |identity_manager|, |unmask_delegate| and |save_delegate|
+  // must all outlive |this|. Either delegate might be nullptr.
+  // |is_off_the_record| denotes incognito mode.
+  PaymentsClient(
+      scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory,
+      PrefService* pref_service,
+      identity::IdentityManager* identity_manager,
+      PaymentsClientUnmaskDelegate* unmask_delegate,
+      PaymentsClientSaveDelegate* save_delegate,
+      bool is_off_the_record = false);
 
-  ~PaymentsClient() override;
+  virtual ~PaymentsClient();
 
   // Starts fetching the OAuth2 token in anticipation of future Payments
   // requests. Called as an optimization, but not strictly necessary. Should
@@ -156,15 +158,26 @@
   // Cancels and clears the current |request_|.
   void CancelRequest();
 
+  // Exposed for testing.
+  void set_url_loader_factory_for_testing(
+      scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory);
+
  private:
+  friend class PaymentsClientTest;
+
   // Initiates a Payments request using the state in |request|. If
   // |authenticate| is true, ensures that an OAuth token is avialble first.
   // Takes ownership of |request|.
   void IssueRequest(std::unique_ptr<PaymentsRequest> request,
                     bool authenticate);
 
-  // net::URLFetcherDelegate:
-  void OnURLFetchComplete(const net::URLFetcher* source) override;
+  // Creates |resource_request_| to be used later in StartRequest().
+  void InitializeResourceRequest();
+
+  // Callback from |simple_url_loader_|.
+  void OnSimpleLoaderComplete(std::unique_ptr<std::string> response_body);
+  void OnSimpleLoaderCompleteInternal(int response_code,
+                                      const std::string& data);
 
   // Callback that handles a completed access token request.
   void AccessTokenFetchFinished(GoogleServiceAuthError error,
@@ -173,17 +186,17 @@
   // Handles a completed access token request in the case of failure.
   void AccessTokenError(const GoogleServiceAuthError& error);
 
-  // Creates |url_fetcher_| based on the current state of |request_|.
-  void InitializeUrlFetcher();
-
   // Initiates a new OAuth2 token request.
   void StartTokenFetch(bool invalidate_old);
 
-  // Adds the token to |url_fetcher_| and starts the request.
+  // Adds the token to |simple_url_loader_| and starts the request.
   void SetOAuth2TokenAndStartRequest();
 
-  // The context for the request.
-  scoped_refptr<net::URLRequestContextGetter> context_getter_;
+  // Creates |simple_url_loader_| and calls it to start the request.
+  void StartRequest();
+
+  // The URL loader factory for the request.
+  scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory_;
 
   // The pref service for this client.
   PrefService* const pref_service_;
@@ -198,8 +211,11 @@
   // The current request.
   std::unique_ptr<PaymentsRequest> request_;
 
-  // The fetcher being used to issue the current request.
-  std::unique_ptr<net::URLFetcher> url_fetcher_;
+  // The resource request being used to issue the current request.
+  std::unique_ptr<network::ResourceRequest> resource_request_;
+
+  // The URL loader being used to issue the current request.
+  std::unique_ptr<network::SimpleURLLoader> simple_url_loader_;
 
   // The current OAuth2 token fetcher.
   std::unique_ptr<identity::PrimaryAccountAccessTokenFetcher> token_fetcher_;
diff --git a/components/autofill/core/browser/payments/payments_client_unittest.cc b/components/autofill/core/browser/payments/payments_client_unittest.cc
index 108779c..1090707 100644
--- a/components/autofill/core/browser/payments/payments_client_unittest.cc
+++ b/components/autofill/core/browser/payments/payments_client_unittest.cc
@@ -8,6 +8,8 @@
 #include "base/command_line.h"
 #include "base/macros.h"
 #include "base/strings/string_piece.h"
+#include "base/strings/utf_string_conversions.h"
+#include "base/test/bind_test_util.h"
 #include "base/test/scoped_feature_list.h"
 #include "base/test/scoped_task_environment.h"
 #include "base/threading/thread_task_runner_handle.h"
@@ -23,9 +25,9 @@
 #include "components/prefs/testing_pref_service.h"
 #include "components/variations/variations_associated_data.h"
 #include "components/variations/variations_http_header_provider.h"
-#include "net/url_request/test_url_fetcher_factory.h"
-#include "net/url_request/url_request_test_util.h"
 #include "services/identity/public/cpp/identity_test_environment.h"
+#include "services/network/public/cpp/weak_wrapper_shared_url_loader_factory.h"
+#include "services/network/test/test_url_loader_factory.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
 namespace autofill {
@@ -62,12 +64,18 @@
     real_pan_.clear();
     legal_message_.reset();
 
-    request_context_ = new net::TestURLRequestContextGetter(
-        base::ThreadTaskRunnerHandle::Get());
+    factory()->SetInterceptor(base::BindLambdaForTesting(
+        [&](const network::ResourceRequest& request) {
+          intercepted_headers_ = request.headers;
+          intercepted_body_ = GetBodyFromRequest(request);
+        }));
+    test_shared_loader_factory_ =
+        base::MakeRefCounted<network::WeakWrapperSharedURLLoaderFactory>(
+            &test_url_loader_factory_);
     TestingPrefServiceSimple pref_service_;
-    client_.reset(new PaymentsClient(request_context_.get(), &pref_service_,
-                                     identity_test_env_.identity_manager(),
-                                     this, this));
+    client_.reset(
+        new PaymentsClient(test_shared_loader_factory_, &pref_service_,
+                           identity_test_env_.identity_manager(), this, this));
   }
 
   void TearDown() override { client_.reset(); }
@@ -153,33 +161,39 @@
     client_->UploadCard(request_details);
   }
 
-  const std::string& GetUploadData() {
-    return factory_.GetFetcherByID(0)->upload_data();
+  network::TestURLLoaderFactory* factory() { return &test_url_loader_factory_; }
+
+  std::string GetBodyFromRequest(const network::ResourceRequest& request) {
+    auto body = request.request_body;
+    if (!body)
+      return std::string();
+
+    CHECK_EQ(1u, body->elements()->size());
+    auto& element = body->elements()->at(0);
+    CHECK_EQ(network::DataElement::TYPE_BYTES, element.type());
+    return std::string(element.bytes(), element.length());
   }
 
+  const std::string& GetUploadData() { return intercepted_body_; }
+
+  net::HttpRequestHeaders* GetRequestHeaders() { return &intercepted_headers_; }
+
   void IssueOAuthToken() {
     identity_test_env_.WaitForAccessTokenRequestIfNecessaryAndRespondWithToken(
         "totally_real_token",
         base::Time::Now() + base::TimeDelta::FromDays(10));
 
     // Verify the auth header.
-    net::TestURLFetcher* fetcher = factory_.GetFetcherByID(0);
-    net::HttpRequestHeaders request_headers;
-    fetcher->GetExtraRequestHeaders(&request_headers);
     std::string auth_header_value;
-    EXPECT_TRUE(request_headers.GetHeader(
+    EXPECT_TRUE(intercepted_headers_.GetHeader(
         net::HttpRequestHeaders::kAuthorization, &auth_header_value))
-        << request_headers.ToString();
+        << intercepted_headers_.ToString();
     EXPECT_EQ("Bearer totally_real_token", auth_header_value);
   }
 
   void ReturnResponse(net::HttpStatusCode response_code,
                       const std::string& response_body) {
-    net::TestURLFetcher* fetcher = factory_.GetFetcherByID(0);
-    ASSERT_TRUE(fetcher);
-    fetcher->set_response_code(response_code);
-    fetcher->SetResponseString(response_body);
-    fetcher->delegate()->OnURLFetchComplete(fetcher);
+    client_->OnSimpleLoaderCompleteInternal(response_code, response_body);
   }
 
   AutofillClient::PaymentsRpcResult result_;
@@ -188,11 +202,14 @@
   std::unique_ptr<base::DictionaryValue> legal_message_;
 
   base::test::ScopedTaskEnvironment scoped_task_environment_;
-  net::TestURLFetcherFactory factory_;
-  scoped_refptr<net::TestURLRequestContextGetter> request_context_;
+  network::TestURLLoaderFactory test_url_loader_factory_;
+  scoped_refptr<network::SharedURLLoaderFactory> test_shared_loader_factory_;
   std::unique_ptr<PaymentsClient> client_;
   identity::IdentityTestEnvironment identity_test_env_;
 
+  net::HttpRequestHeaders intercepted_headers_;
+  std::string intercepted_body_;
+
  private:
   DISALLOW_COPY_AND_ASSIGN(PaymentsClientTest);
 
@@ -238,6 +255,7 @@
 TEST_F(PaymentsClientTest,
        UnmaskRequestIncludesBillingCustomerNumberInRequest) {
   StartUnmasking();
+  IssueOAuthToken();
 
   // Verify that the billing customer number is included in the request.
   EXPECT_TRUE(
@@ -304,15 +322,10 @@
   CreateFieldTrialWithId("AutofillTest", "Group", 369);
   StartGettingUploadDetails();
 
-  net::TestURLFetcher* fetcher = factory_.GetFetcherByID(0);
-  net::HttpRequestHeaders headers;
-  fetcher->GetExtraRequestHeaders(&headers);
   std::string value;
-  EXPECT_TRUE(headers.GetHeader("X-Client-Data", &value));
+  EXPECT_TRUE(GetRequestHeaders()->GetHeader("X-Client-Data", &value));
   // Note that experiment information is stored in X-Client-Data.
   EXPECT_FALSE(value.empty());
-  // The fetcher's delegate is responsible for freeing the fetcher (and itself).
-  fetcher->delegate()->OnURLFetchComplete(fetcher);
 
   variations::VariationsHttpHeaderProvider::GetInstance()->ResetForTesting();
 }
@@ -327,15 +340,10 @@
   CreateFieldTrialWithId("AutofillTest", "Group", 369);
   StartGettingUploadDetails();
 
-  net::TestURLFetcher* fetcher = factory_.GetFetcherByID(0);
-  net::HttpRequestHeaders headers;
-  fetcher->GetExtraRequestHeaders(&headers);
   std::string value;
-  EXPECT_FALSE(headers.GetHeader("X-Client-Data", &value));
+  EXPECT_FALSE(GetRequestHeaders()->GetHeader("X-Client-Data", &value));
   // Note that experiment information is stored in X-Client-Data.
   EXPECT_TRUE(value.empty());
-  // The fetcher's delegate is responsible for freeing the fetcher (and itself).
-  fetcher->delegate()->OnURLFetchComplete(fetcher);
 
   variations::VariationsHttpHeaderProvider::GetInstance()->ResetForTesting();
 }
@@ -348,16 +356,12 @@
   base::FieldTrialList field_trial_list_(nullptr);
   CreateFieldTrialWithId("AutofillTest", "Group", 369);
   StartUploading(/*include_cvc=*/true);
+  IssueOAuthToken();
 
-  net::TestURLFetcher* fetcher = factory_.GetFetcherByID(0);
-  net::HttpRequestHeaders headers;
-  fetcher->GetExtraRequestHeaders(&headers);
   std::string value;
-  EXPECT_TRUE(headers.GetHeader("X-Client-Data", &value));
+  EXPECT_TRUE(GetRequestHeaders()->GetHeader("X-Client-Data", &value));
   // Note that experiment information is stored in X-Client-Data.
   EXPECT_FALSE(value.empty());
-  // The fetcher's delegate is responsible for freeing the fetcher (and itself).
-  fetcher->delegate()->OnURLFetchComplete(fetcher);
 
   variations::VariationsHttpHeaderProvider::GetInstance()->ResetForTesting();
 }
@@ -372,15 +376,10 @@
   CreateFieldTrialWithId("AutofillTest", "Group", 369);
   StartUploading(/*include_cvc=*/true);
 
-  net::TestURLFetcher* fetcher = factory_.GetFetcherByID(0);
-  net::HttpRequestHeaders headers;
-  fetcher->GetExtraRequestHeaders(&headers);
   std::string value;
-  EXPECT_FALSE(headers.GetHeader("X-Client-Data", &value));
+  EXPECT_FALSE(GetRequestHeaders()->GetHeader("X-Client-Data", &value));
   // Note that experiment information is stored in X-Client-Data.
   EXPECT_TRUE(value.empty());
-  // The fetcher's delegate is responsible for freeing the fetcher (and itself).
-  fetcher->delegate()->OnURLFetchComplete(fetcher);
 
   variations::VariationsHttpHeaderProvider::GetInstance()->ResetForTesting();
 }
@@ -393,16 +392,12 @@
   base::FieldTrialList field_trial_list_(nullptr);
   CreateFieldTrialWithId("AutofillTest", "Group", 369);
   StartUnmasking();
+  IssueOAuthToken();
 
-  net::TestURLFetcher* fetcher = factory_.GetFetcherByID(0);
-  net::HttpRequestHeaders headers;
-  fetcher->GetExtraRequestHeaders(&headers);
   std::string value;
-  EXPECT_TRUE(headers.GetHeader("X-Client-Data", &value));
+  EXPECT_TRUE(GetRequestHeaders()->GetHeader("X-Client-Data", &value));
   // Note that experiment information is stored in X-Client-Data.
   EXPECT_FALSE(value.empty());
-  // The fetcher's delegate is responsible for freeing the fetcher (and itself).
-  fetcher->delegate()->OnURLFetchComplete(fetcher);
 
   variations::VariationsHttpHeaderProvider::GetInstance()->ResetForTesting();
 }
@@ -417,15 +412,10 @@
   CreateFieldTrialWithId("AutofillTest", "Group", 369);
   StartUnmasking();
 
-  net::TestURLFetcher* fetcher = factory_.GetFetcherByID(0);
-  net::HttpRequestHeaders headers;
-  fetcher->GetExtraRequestHeaders(&headers);
   std::string value;
-  EXPECT_FALSE(headers.GetHeader("X-Client-Data", &value));
+  EXPECT_FALSE(GetRequestHeaders()->GetHeader("X-Client-Data", &value));
   // Note that experiment information is stored in X-Client-Data.
   EXPECT_TRUE(value.empty());
-  // The fetcher's delegate is responsible for freeing the fetcher (and itself).
-  fetcher->delegate()->OnURLFetchComplete(fetcher);
 
   variations::VariationsHttpHeaderProvider::GetInstance()->ResetForTesting();
 }
@@ -468,6 +458,7 @@
 
 TEST_F(PaymentsClientTest, UploadIncludesNonLocationData) {
   StartUploading(/*include_cvc=*/true);
+  IssueOAuthToken();
 
   // Verify that the recipient name field and test names do appear in the upload
   // data.
@@ -492,6 +483,7 @@
 TEST_F(PaymentsClientTest,
        UploadRequestIncludesBillingCustomerNumberInRequest) {
   StartUploading(/*include_cvc=*/true);
+  IssueOAuthToken();
 
   // Verify that the billing customer number is included in the request.
   EXPECT_TRUE(
@@ -501,6 +493,7 @@
 
 TEST_F(PaymentsClientTest, UploadIncludesCvcInRequestIfProvided) {
   StartUploading(/*include_cvc=*/true);
+  IssueOAuthToken();
 
   // Verify that the encrypted_cvc and s7e_13_cvc parameters were included in
   // the request.
diff --git a/components/autofill/core/browser/payments/test_payments_client.cc b/components/autofill/core/browser/payments/test_payments_client.cc
index 845b84d8..c85d84c5 100644
--- a/components/autofill/core/browser/payments/test_payments_client.cc
+++ b/components/autofill/core/browser/payments/test_payments_client.cc
@@ -5,17 +5,18 @@
 #include "components/autofill/core/browser/payments/test_payments_client.h"
 
 #include "base/strings/utf_string_conversions.h"
+#include "services/network/public/cpp/shared_url_loader_factory.h"
 
 namespace autofill {
 namespace payments {
 
 TestPaymentsClient::TestPaymentsClient(
-    net::URLRequestContextGetter* context_getter,
+    scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory_,
     PrefService* pref_service,
     identity::IdentityManager* identity_manager,
     payments::PaymentsClientUnmaskDelegate* unmask_delegate,
     payments::PaymentsClientSaveDelegate* save_delegate)
-    : PaymentsClient(context_getter,
+    : PaymentsClient(url_loader_factory_,
                      pref_service,
                      identity_manager,
                      unmask_delegate,
diff --git a/components/autofill/core/browser/payments/test_payments_client.h b/components/autofill/core/browser/payments/test_payments_client.h
index f8b0f4a..cfdcc978 100644
--- a/components/autofill/core/browser/payments/test_payments_client.h
+++ b/components/autofill/core/browser/payments/test_payments_client.h
@@ -10,16 +10,21 @@
 
 #include "components/autofill/core/browser/payments/payments_client.h"
 
+namespace network {
+class SharedURLLoaderFactory;
+}  // namespace network
+
 namespace autofill {
 namespace payments {
 
 class TestPaymentsClient : public payments::PaymentsClient {
  public:
-  TestPaymentsClient(net::URLRequestContextGetter* context_getter,
-                     PrefService* pref_service,
-                     identity::IdentityManager* identity_manager,
-                     payments::PaymentsClientUnmaskDelegate* unmask_delegate,
-                     payments::PaymentsClientSaveDelegate* save_delegate);
+  TestPaymentsClient(
+      scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory_,
+      PrefService* pref_service,
+      identity::IdentityManager* identity_manager,
+      payments::PaymentsClientUnmaskDelegate* unmask_delegate,
+      payments::PaymentsClientSaveDelegate* save_delegate);
 
   ~TestPaymentsClient() override;
 
diff --git a/components/autofill/core/browser/test_autofill_driver.cc b/components/autofill/core/browser/test_autofill_driver.cc
index 94ef6316..e0cf096 100644
--- a/components/autofill/core/browser/test_autofill_driver.cc
+++ b/components/autofill/core/browser/test_autofill_driver.cc
@@ -3,12 +3,19 @@
 // found in the LICENSE file.
 
 #include "components/autofill/core/browser/test_autofill_driver.h"
+#include "services/network/public/cpp/shared_url_loader_factory.h"
+#include "services/network/public/cpp/weak_wrapper_shared_url_loader_factory.h"
+#include "services/network/test/test_url_loader_factory.h"
 
 #include "ui/gfx/geometry/rect_f.h"
 
 namespace autofill {
 
-TestAutofillDriver::TestAutofillDriver() : url_request_context_(nullptr) {}
+TestAutofillDriver::TestAutofillDriver()
+    : url_request_context_(nullptr),
+      test_shared_loader_factory_(
+          base::MakeRefCounted<network::WeakWrapperSharedURLLoaderFactory>(
+              &test_url_loader_factory_)) {}
 
 TestAutofillDriver::~TestAutofillDriver() {}
 
@@ -20,6 +27,11 @@
   return url_request_context_;
 }
 
+scoped_refptr<network::SharedURLLoaderFactory>
+TestAutofillDriver::GetURLLoaderFactory() {
+  return test_shared_loader_factory_;
+}
+
 bool TestAutofillDriver::RendererIsAvailable() {
   return true;
 }
diff --git a/components/autofill/core/browser/test_autofill_driver.h b/components/autofill/core/browser/test_autofill_driver.h
index f5bbb55..42fef02 100644
--- a/components/autofill/core/browser/test_autofill_driver.h
+++ b/components/autofill/core/browser/test_autofill_driver.h
@@ -7,7 +7,9 @@
 
 #include "base/compiler_specific.h"
 #include "base/macros.h"
+#include "base/memory/scoped_refptr.h"
 #include "components/autofill/core/browser/autofill_driver.h"
+#include "services/network/test/test_url_loader_factory.h"
 
 namespace autofill {
 
@@ -22,6 +24,7 @@
   // Returns the value passed in to the last call to |SetURLRequestContext()|
   // or NULL if that method has never been called.
   net::URLRequestContextGetter* GetURLRequestContext() override;
+  scoped_refptr<network::SharedURLLoaderFactory> GetURLLoaderFactory() override;
   bool RendererIsAvailable() override;
   void SendFormDataToRenderer(int query_id,
                               RendererFormDataAction action,
@@ -57,6 +60,8 @@
 
  private:
   net::URLRequestContextGetter* url_request_context_;
+  network::TestURLLoaderFactory test_url_loader_factory_;
+  scoped_refptr<network::SharedURLLoaderFactory> test_shared_loader_factory_;
   bool is_incognito_ = false;
   bool did_interact_with_credit_card_form_ = false;
 
diff --git a/components/autofill/core/browser/test_autofill_manager.cc b/components/autofill/core/browser/test_autofill_manager.cc
index 467c8825..c037e4714 100644
--- a/components/autofill/core/browser/test_autofill_manager.cc
+++ b/components/autofill/core/browser/test_autofill_manager.cc
@@ -12,6 +12,7 @@
 #include "components/autofill/core/browser/test_form_data_importer.h"
 #include "components/autofill/core/browser/test_form_structure.h"
 #include "components/autofill/core/browser/test_personal_data_manager.h"
+#include "services/network/public/cpp/shared_url_loader_factory.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
 namespace autofill {
@@ -21,10 +22,10 @@
                                          TestPersonalDataManager* personal_data)
     : AutofillManager(driver, client, personal_data),
       personal_data_(personal_data),
-      context_getter_(driver->GetURLRequestContext()),
+      url_loader_factory_(driver->GetURLLoaderFactory()),
       client_(client) {
   set_payments_client(new payments::PaymentsClient(
-      context_getter_, client->GetPrefs(), client->GetIdentityManager(),
+      url_loader_factory_, client->GetPrefs(), client->GetIdentityManager(),
       /*unmask_delegate=*/this,
       /*save_delegate=*/nullptr));
 }
diff --git a/components/autofill/core/browser/test_autofill_manager.h b/components/autofill/core/browser/test_autofill_manager.h
index bc39025..4866514 100644
--- a/components/autofill/core/browser/test_autofill_manager.h
+++ b/components/autofill/core/browser/test_autofill_manager.h
@@ -16,8 +16,8 @@
 
 using base::TimeTicks;
 
-namespace net {
-class URLRequestContextGetter;
+namespace network {
+class SharedURLLoaderFactory;
 }
 
 namespace autofill {
@@ -89,7 +89,7 @@
 
  private:
   TestPersonalDataManager* personal_data_;                  // Weak reference.
-  net::URLRequestContextGetter* context_getter_ = nullptr;  // Weak reference.
+  scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory_;
   TestFormDataImporter* test_form_data_importer_ = nullptr;
   bool autofill_enabled_ = true;
   bool credit_card_enabled_ = true;
diff --git a/components/autofill/ios/browser/BUILD.gn b/components/autofill/ios/browser/BUILD.gn
index 0371187..0ed8992b 100644
--- a/components/autofill/ios/browser/BUILD.gn
+++ b/components/autofill/ios/browser/BUILD.gn
@@ -39,6 +39,7 @@
     "//components/prefs/ios",
     "//google_apis",
     "//ios/web",
+    "//services/network/public/cpp",
     "//ui/gfx/geometry",
   ]
 }
diff --git a/components/autofill/ios/browser/DEPS b/components/autofill/ios/browser/DEPS
index 4dd6307..c6de2be 100644
--- a/components/autofill/ios/browser/DEPS
+++ b/components/autofill/ios/browser/DEPS
@@ -1,4 +1,5 @@
 include_rules = [
   "+ios/web/public",
+  "+services/network/public/cpp",
   "+third_party/ocmock",
 ]
diff --git a/components/autofill/ios/browser/autofill_driver_ios.h b/components/autofill/ios/browser/autofill_driver_ios.h
index a39cbc3..471cf9c2 100644
--- a/components/autofill/ios/browser/autofill_driver_ios.h
+++ b/components/autofill/ios/browser/autofill_driver_ios.h
@@ -38,6 +38,7 @@
   // AutofillDriver:
   bool IsIncognito() const override;
   net::URLRequestContextGetter* GetURLRequestContext() override;
+  scoped_refptr<network::SharedURLLoaderFactory> GetURLLoaderFactory() override;
   bool RendererIsAvailable() override;
   void SendFormDataToRenderer(int query_id,
                               RendererFormDataAction action,
diff --git a/components/autofill/ios/browser/autofill_driver_ios.mm b/components/autofill/ios/browser/autofill_driver_ios.mm
index d94e80e6..1df6ec5 100644
--- a/components/autofill/ios/browser/autofill_driver_ios.mm
+++ b/components/autofill/ios/browser/autofill_driver_ios.mm
@@ -10,6 +10,8 @@
 #include "ios/web/public/browser_state.h"
 #import "ios/web/public/origin_util.h"
 #include "ios/web/public/web_state/web_state.h"
+#include "services/network/public/cpp/shared_url_loader_factory.h"
+#include "services/network/public/cpp/weak_wrapper_shared_url_loader_factory.h"
 #include "ui/gfx/geometry/rect_f.h"
 
 #if !defined(__has_feature) || !__has_feature(objc_arc)
@@ -59,6 +61,12 @@
   return web_state_->GetBrowserState()->GetRequestContext();
 }
 
+scoped_refptr<network::SharedURLLoaderFactory>
+AutofillDriverIOS::GetURLLoaderFactory() {
+  return base::MakeRefCounted<network::WeakWrapperSharedURLLoaderFactory>(
+      web_state_->GetBrowserState()->GetURLLoaderFactory());
+}
+
 bool AutofillDriverIOS::RendererIsAvailable() {
   return true;
 }
diff --git a/components/autofill_strings.grdp b/components/autofill_strings.grdp
index 8ade912..3a529438b 100644
--- a/components/autofill_strings.grdp
+++ b/components/autofill_strings.grdp
@@ -303,7 +303,7 @@
     Check your expiration year and try again
   </message>
   <message name="IDS_AUTOFILL_CARD_UNMASK_PROMPT_ERROR_PERMANENT" desc="Error message to show when a credit card cannot be verified and the user isn't allowed to retry.">
-    This card can't be verified right now.
+    This card can't be verified right now
   </message>
   <message name="IDS_AUTOFILL_CARD_UNMASK_PROMPT_ERROR_NETWORK" desc="Error message to show when a credit card cannot be verified because Wallet servers can't be reached.">
     There was a problem confirming your card. Check your internet connection and try again.
diff --git a/components/cronet/android/BUILD.gn b/components/cronet/android/BUILD.gn
index fa033fd7..1594d8ff 100644
--- a/components/cronet/android/BUILD.gn
+++ b/components/cronet/android/BUILD.gn
@@ -1197,6 +1197,7 @@
     "//components/cronet/android/java/src",
     "//net/android/java/src",
     "//url/android/java/src",
+    "//third_party/android_async_task/java/src",
   ]
   source_deps = [
     ":cronet_impl_native_base_java",
diff --git a/components/metrics/BUILD.gn b/components/metrics/BUILD.gn
index 9b54656..15b3a14 100644
--- a/components/metrics/BUILD.gn
+++ b/components/metrics/BUILD.gn
@@ -6,8 +6,6 @@
 
 static_library("metrics") {
   sources = [
-    "call_stack_profile_builder.cc",
-    "call_stack_profile_builder.h",
     "call_stack_profile_metrics_provider.cc",
     "call_stack_profile_metrics_provider.h",
     "clean_exit_beacon.cc",
@@ -107,7 +105,7 @@
   ]
 
   public_deps = [
-    ":call_stack_profile_params",
+    ":call_stack_profile",
     "//third_party/metrics_proto",
   ]
 
@@ -258,8 +256,10 @@
   ]
 }
 
-source_set("call_stack_profile_params") {
+source_set("call_stack_profile") {
   sources = [
+    "call_stack_profile_builder.cc",
+    "call_stack_profile_builder.h",
     "call_stack_profile_params.h",
   ]
 
@@ -285,6 +285,7 @@
     "child_call_stack_profile_collector.h",
   ]
   deps = [
+    ":call_stack_profile",
     "//components/metrics/public/interfaces:call_stack_mojo_bindings",
     "//services/service_manager/public/cpp",
   ]
@@ -360,7 +361,7 @@
   ]
 
   deps = [
-    ":call_stack_profile_params",
+    ":call_stack_profile",
     ":child_call_stacks",
     ":component_metrics",
     ":metrics",
diff --git a/components/metrics/call_stack_profile_builder.cc b/components/metrics/call_stack_profile_builder.cc
index f3eb2b1..aeb833d 100644
--- a/components/metrics/call_stack_profile_builder.cc
+++ b/components/metrics/call_stack_profile_builder.cc
@@ -11,7 +11,7 @@
 using StackSamplingProfiler = base::StackSamplingProfiler;
 
 CallStackProfileBuilder::CallStackProfileBuilder(
-    const StackSamplingProfiler::CompletedCallback& callback)
+    const CompletedCallback& callback)
     : callback_(callback) {}
 
 CallStackProfileBuilder::~CallStackProfileBuilder() = default;
diff --git a/components/metrics/call_stack_profile_builder.h b/components/metrics/call_stack_profile_builder.h
index d647867..8eb7e01 100644
--- a/components/metrics/call_stack_profile_builder.h
+++ b/components/metrics/call_stack_profile_builder.h
@@ -9,6 +9,8 @@
 
 #include <map>
 
+#include "base/callback.h"
+
 // CallStackProfileBuilder builds a CallStackProfile from the collected sampling
 // data.
 //
@@ -20,8 +22,20 @@
 class CallStackProfileBuilder
     : public base::StackSamplingProfiler::ProfileBuilder {
  public:
-  CallStackProfileBuilder(
-      const base::StackSamplingProfiler::CompletedCallback& callback);
+  // The callback type used to collect a completed profile. The passed
+  // CallStackProfile is move-only. Other threads, including the UI thread, may
+  // block on callback completion so this should run as quickly as possible.
+  //
+  // IMPORTANT NOTE: The callback is invoked on a thread the profiler
+  // constructs, rather than on the thread used to construct the profiler, and
+  // thus the callback must be callable on any thread. For threads with message
+  // loops that create CallStackProfileBuilders, posting a task to the message
+  // loop with the moved (i.e. std::move) profile is the thread-safe callback
+  // implementation.
+  using CompletedCallback =
+      base::Callback<void(base::StackSamplingProfiler::CallStackProfile)>;
+
+  CallStackProfileBuilder(const CompletedCallback& callback);
 
   ~CallStackProfileBuilder() override;
 
@@ -43,7 +57,7 @@
   std::map<uintptr_t, size_t> module_index_;
 
   // Callback made when sampling a profile completes.
-  const base::StackSamplingProfiler::CompletedCallback callback_;
+  const CompletedCallback callback_;
 
   DISALLOW_COPY_AND_ASSIGN(CallStackProfileBuilder);
 };
diff --git a/components/metrics/call_stack_profile_metrics_provider.cc b/components/metrics/call_stack_profile_metrics_provider.cc
index 15f3b04..dc456fe 100644
--- a/components/metrics/call_stack_profile_metrics_provider.cc
+++ b/components/metrics/call_stack_profile_metrics_provider.cc
@@ -414,7 +414,7 @@
 
 CallStackProfileMetricsProvider::~CallStackProfileMetricsProvider() {}
 
-StackSamplingProfiler::CompletedCallback
+CallStackProfileBuilder::CompletedCallback
 CallStackProfileMetricsProvider::GetProfilerCallbackForBrowserProcess(
     const CallStackProfileParams& params) {
   // Ignore the profile if the collection is disabled. If the collection state
diff --git a/components/metrics/call_stack_profile_metrics_provider.h b/components/metrics/call_stack_profile_metrics_provider.h
index fb09221..44b3431 100644
--- a/components/metrics/call_stack_profile_metrics_provider.h
+++ b/components/metrics/call_stack_profile_metrics_provider.h
@@ -9,6 +9,7 @@
 #include "base/macros.h"
 #include "base/memory/ref_counted.h"
 #include "base/profiler/stack_sampling_profiler.h"
+#include "components/metrics/call_stack_profile_builder.h"
 #include "components/metrics/call_stack_profile_params.h"
 #include "components/metrics/metrics_provider.h"
 
@@ -36,10 +37,11 @@
   CallStackProfileMetricsProvider();
   ~CallStackProfileMetricsProvider() override;
 
-  // Returns a callback for use with StackSamplingProfiler that sets up
+  // Returns a callback for use with CallStackProfileBuilder that sets up
   // parameters for general browser process sampling. The callback should be
-  // immediately passed to the StackSamplingProfiler, and should not be reused.
-  static base::StackSamplingProfiler::CompletedCallback
+  // immediately passed to the CallStackProfileBuilder, and should not be
+  // reused.
+  static CallStackProfileBuilder::CompletedCallback
   GetProfilerCallbackForBrowserProcess(const CallStackProfileParams& params);
 
   // Provides completed stack profile to the metrics provider. Intended for use
diff --git a/components/metrics/call_stack_profile_metrics_provider_unittest.cc b/components/metrics/call_stack_profile_metrics_provider_unittest.cc
index c3d07fd..309235c3 100644
--- a/components/metrics/call_stack_profile_metrics_provider_unittest.cc
+++ b/components/metrics/call_stack_profile_metrics_provider_unittest.cc
@@ -481,7 +481,7 @@
                                 CallStackProfileParams::MAIN_THREAD,
                                 CallStackProfileParams::PROCESS_STARTUP,
                                 CallStackProfileParams::MAY_SHUFFLE);
-  base::StackSamplingProfiler::CompletedCallback callback =
+  CallStackProfileBuilder::CompletedCallback callback =
       CallStackProfileMetricsProvider::GetProfilerCallbackForBrowserProcess(
           params);
   provider.OnRecordingDisabled();
@@ -507,7 +507,7 @@
                                 CallStackProfileParams::MAIN_THREAD,
                                 CallStackProfileParams::PROCESS_STARTUP,
                                 CallStackProfileParams::MAY_SHUFFLE);
-  base::StackSamplingProfiler::CompletedCallback callback =
+  CallStackProfileBuilder::CompletedCallback callback =
       CallStackProfileMetricsProvider::GetProfilerCallbackForBrowserProcess(
           params);
   provider.OnRecordingDisabled();
@@ -534,7 +534,7 @@
                                 CallStackProfileParams::MAIN_THREAD,
                                 CallStackProfileParams::PROCESS_STARTUP,
                                 CallStackProfileParams::MAY_SHUFFLE);
-  base::StackSamplingProfiler::CompletedCallback callback =
+  CallStackProfileBuilder::CompletedCallback callback =
       CallStackProfileMetricsProvider::GetProfilerCallbackForBrowserProcess(
           params);
   provider.OnRecordingEnabled();
diff --git a/components/metrics/child_call_stack_profile_collector.cc b/components/metrics/child_call_stack_profile_collector.cc
index 22b60ff..3cdfc55 100644
--- a/components/metrics/child_call_stack_profile_collector.cc
+++ b/components/metrics/child_call_stack_profile_collector.cc
@@ -38,7 +38,7 @@
 
 ChildCallStackProfileCollector::~ChildCallStackProfileCollector() {}
 
-base::StackSamplingProfiler::CompletedCallback
+CallStackProfileBuilder::CompletedCallback
 ChildCallStackProfileCollector::GetProfilerCallback(
     const CallStackProfileParams& params,
     base::TimeTicks profile_start_time) {
diff --git a/components/metrics/child_call_stack_profile_collector.h b/components/metrics/child_call_stack_profile_collector.h
index efeac6f4..6e5f3313 100644
--- a/components/metrics/child_call_stack_profile_collector.h
+++ b/components/metrics/child_call_stack_profile_collector.h
@@ -11,6 +11,7 @@
 #include "base/memory/ref_counted.h"
 #include "base/single_thread_task_runner.h"
 #include "base/synchronization/lock.h"
+#include "components/metrics/call_stack_profile_builder.h"
 #include "components/metrics/public/interfaces/call_stack_profile_collector.mojom.h"
 
 namespace service_manager {
@@ -41,7 +42,7 @@
 //       g_call_stack_profile_collector = LAZY_INSTANCE_INITIALIZER;
 //
 // Then, invoke CreateCompletedCallback() to generate the CompletedCallback to
-// pass when creating the StackSamplingProfiler.
+// pass when creating the CallStackProfileBuilder.
 //
 // When the mojo InterfaceProvider becomes available, provide it via
 // SetParentProfileCollector().
@@ -50,11 +51,11 @@
   ChildCallStackProfileCollector();
   ~ChildCallStackProfileCollector();
 
-  // Get a callback for use with StackSamplingProfiler that provides the
+  // Get a callback for use with CallStackProfileBuilder that provides the
   // completed profile to this object. The callback should be immediately passed
-  // to the StackSamplingProfiler, and should not be reused between
-  // StackSamplingProfilers. This function may be called on any thread.
-  base::StackSamplingProfiler::CompletedCallback GetProfilerCallback(
+  // to the CallStackProfileBuilder, and should not be reused between
+  // CallStackProfileBuilders. This function may be called on any thread.
+  CallStackProfileBuilder::CompletedCallback GetProfilerCallback(
       const CallStackProfileParams& params,
       base::TimeTicks profile_start_time);
 
diff --git a/components/metrics/public/cpp/OWNERS b/components/metrics/public/cpp/OWNERS
index 1544352..9e7bd07 100644
--- a/components/metrics/public/cpp/OWNERS
+++ b/components/metrics/public/cpp/OWNERS
@@ -2,3 +2,5 @@
 per-file *.mojom=file://ipc/SECURITY_OWNERS
 per-file *_struct_traits*.*=set noparent
 per-file *_struct_traits*.*=file://ipc/SECURITY_OWNERS
+per-file *.typemap=set noparent

+per-file *.typemap=file://ipc/SECURITY_OWNERS
diff --git a/components/metrics/public/cpp/call_stack_profile.typemap b/components/metrics/public/cpp/call_stack_profile.typemap
index 611148c..aa84565 100644
--- a/components/metrics/public/cpp/call_stack_profile.typemap
+++ b/components/metrics/public/cpp/call_stack_profile.typemap
@@ -12,7 +12,7 @@
     [ "//components/metrics/public/cpp/call_stack_profile_struct_traits.h" ]
 deps = [
   "//base",
-  "//components/metrics:call_stack_profile_params",
+  "//components/metrics:call_stack_profile",
 ]
 type_mappings = [
   "metrics.mojom.CallStackModule=base::StackSamplingProfiler::Module",
diff --git a/components/omnibox/browser/autocomplete_match_type.cc b/components/omnibox/browser/autocomplete_match_type.cc
index 9612ba84..803b262 100644
--- a/components/omnibox/browser/autocomplete_match_type.cc
+++ b/components/omnibox/browser/autocomplete_match_type.cc
@@ -72,13 +72,13 @@
 
       // HISTORY_KEYWORD is a custom search engine with no %s in its string - so
       // more or less a regular URL.
-      0,                                      // HISTORY_KEYWORD
-      0,                                      // NAVSUGGEST
-      IDS_ACC_AUTOCOMPLETE_SEARCH,            // SEARCH_WHAT_YOU_TYPED
-      IDS_ACC_AUTOCOMPLETE_SEARCH_HISTORY,    // SEARCH_HISTORY
-      IDS_ACC_AUTOCOMPLETE_SUGGESTED_SEARCH,  // SEARCH_SUGGEST
-      IDS_ACC_AUTOCOMPLETE_SUGGESTED_SEARCH,  // SEARCH_SUGGEST_ENTITY
-      IDS_ACC_AUTOCOMPLETE_SUGGESTED_SEARCH,  // SEARCH_SUGGEST_TAIL
+      0,                                             // HISTORY_KEYWORD
+      0,                                             // NAVSUGGEST
+      IDS_ACC_AUTOCOMPLETE_SEARCH,                   // SEARCH_WHAT_YOU_TYPED
+      IDS_ACC_AUTOCOMPLETE_SEARCH_HISTORY,           // SEARCH_HISTORY
+      IDS_ACC_AUTOCOMPLETE_SUGGESTED_SEARCH,         // SEARCH_SUGGEST
+      IDS_ACC_AUTOCOMPLETE_SUGGESTED_SEARCH_ENTITY,  // SEARCH_SUGGEST_ENTITY
+      IDS_ACC_AUTOCOMPLETE_SUGGESTED_SEARCH,         // SEARCH_SUGGEST_TAIL
 
       // SEARCH_SUGGEST_PERSONALIZED are searches from history elsewhere, maybe
       // on other machines via Sync, or when signed in to Google.
@@ -126,7 +126,16 @@
         message = IDS_ACC_AUTOCOMPLETE_QUICK_ANSWER;
       }
       break;
-
+    case IDS_ACC_AUTOCOMPLETE_SUGGESTED_SEARCH_ENTITY:
+      if (match.description.empty()) {
+        // No description, so fall back to ordinary search suggestion format.
+        message = IDS_ACC_AUTOCOMPLETE_SUGGESTED_SEARCH;
+      } else {
+        // Full entity search suggestion with description.
+        description = match.description;
+        has_description = true;
+      }
+      break;
     case IDS_ACC_AUTOCOMPLETE_HISTORY:
     case IDS_ACC_AUTOCOMPLETE_BOOKMARK:
     case IDS_ACC_AUTOCOMPLETE_CLIPBOARD:
diff --git a/components/omnibox_strings.grdp b/components/omnibox_strings.grdp
index 905cf84..92c3a7e 100644
--- a/components/omnibox_strings.grdp
+++ b/components/omnibox_strings.grdp
@@ -71,6 +71,9 @@
   <message name="IDS_ACC_AUTOCOMPLETE_SUGGESTED_SEARCH" desc="Text for screenreaders describing a suggested search.">
     <ph name="TEXT">$1<ex>dogs</ex> search suggestion</ph>
   </message>
+  <message name="IDS_ACC_AUTOCOMPLETE_SUGGESTED_SEARCH_ENTITY" desc="Readable text represening a search suggestion for a rich entity with type description.  The commas are significant as they will introduce a pause in the spoken text.">
+    <ph name="ENTITY">$1<ex>muhammad ali</ex></ph>, <ph name="DESCRIPTION">$2<ex>american professional boxer</ex></ph>, search suggestion
+  </message>
   <message name="IDS_ACC_AUTOCOMPLETE_QUICK_ANSWER" desc="Readable text represening a query typed by the user in the omnibox, followed by an indication that an answer to that query will follow, followed by the answer. The commas are significant as they will introduce a pause in the spoken text.">
     <ph name="QUERY">$1<ex>weather in los angeles</ex></ph>, answer, <ph name="ANSWER">$2<ex>sunny and 84 degrees</ex></ph>
   </message>
diff --git a/components/payments/core/BUILD.gn b/components/payments/core/BUILD.gn
index 9b7daea..c58fb97 100644
--- a/components/payments/core/BUILD.gn
+++ b/components/payments/core/BUILD.gn
@@ -69,6 +69,7 @@
     "//net",
     "//services/metrics/public/cpp:metrics_cpp",
     "//services/metrics/public/cpp:ukm_builders",
+    "//services/network/public/cpp",
     "//third_party/re2",
     "//ui/base",
     "//url",
@@ -100,6 +101,8 @@
     "//components/pref_registry",
     "//components/prefs",
     "//net:test_support",
+    "//services/network:test_support",
+    "//services/network/public/cpp",
   ]
 }
 
@@ -142,6 +145,7 @@
     "//net:test_support",
     "//services/metrics/public/cpp:ukm_builders",
     "//services/network:test_support",
+    "//services/network/public/cpp",
     "//testing/gmock",
     "//testing/gtest",
     "//third_party/libaddressinput:test_support",
diff --git a/components/payments/core/autofill_payment_instrument_unittest.cc b/components/payments/core/autofill_payment_instrument_unittest.cc
index f70e552..57b9f6a 100644
--- a/components/payments/core/autofill_payment_instrument_unittest.cc
+++ b/components/payments/core/autofill_payment_instrument_unittest.cc
@@ -22,6 +22,9 @@
 #include "components/payments/core/test_payment_request_delegate.h"
 #include "components/strings/grit/components_strings.h"
 #include "net/url_request/url_request_test_util.h"
+#include "services/network/public/cpp/shared_url_loader_factory.h"
+#include "services/network/public/cpp/weak_wrapper_shared_url_loader_factory.h"
+#include "services/network/test/test_url_loader_factory.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "ui/base/l10n/l10n_util.h"
 
@@ -64,9 +67,10 @@
       : locale_("en-US"),
         last_committed_url_("https://shop.com"),
         personal_data_("en-US"),
-        request_context_(new net::TestURLRequestContextGetter(
-            base::ThreadTaskRunnerHandle::Get())),
-        payments_client_(request_context_.get(),
+        test_shared_loader_factory_(
+            base::MakeRefCounted<network::WeakWrapperSharedURLLoaderFactory>(
+                &test_url_loader_factory_)),
+        payments_client_(test_shared_loader_factory_,
                          nullptr,
                          nullptr,
                          this,
@@ -120,7 +124,8 @@
   const GURL last_committed_url_;
   autofill::TestAddressNormalizer address_normalizer_;
   autofill::PersonalDataManager personal_data_;
-  scoped_refptr<net::TestURLRequestContextGetter> request_context_;
+  network::TestURLLoaderFactory test_url_loader_factory_;
+  scoped_refptr<network::SharedURLLoaderFactory> test_shared_loader_factory_;
   autofill::TestAutofillClient autofill_client_;
   autofill::payments::PaymentsClient payments_client_;
   autofill::payments::FullCardRequest full_card_request_;
diff --git a/components/payments/core/test_payment_request_delegate.cc b/components/payments/core/test_payment_request_delegate.cc
index 1d1654d..de207ed 100644
--- a/components/payments/core/test_payment_request_delegate.cc
+++ b/components/payments/core/test_payment_request_delegate.cc
@@ -13,8 +13,10 @@
     : personal_data_manager_(personal_data_manager),
       locale_("en-US"),
       last_committed_url_("https://shop.com"),
-      request_context_(new TestURLRequestContextGetter(loop_.task_runner())),
-      payments_client_(request_context_.get(),
+      test_shared_loader_factory_(
+          base::MakeRefCounted<network::WeakWrapperSharedURLLoaderFactory>(
+              &test_url_loader_factory_)),
+      payments_client_(test_shared_loader_factory_,
                        nullptr,
                        nullptr,
                        /*unmask_delegate=*/&payments_client_delegate_,
@@ -108,19 +110,4 @@
     autofill::AutofillClient::PaymentsRpcResult result,
     const std::string& real_pan) {}
 
-TestURLRequestContextGetter::TestURLRequestContextGetter(
-    scoped_refptr<base::SingleThreadTaskRunner> task_runner)
-    : task_runner_(task_runner) {}
-
-TestURLRequestContextGetter::~TestURLRequestContextGetter() {}
-
-net::URLRequestContext* TestURLRequestContextGetter::GetURLRequestContext() {
-  return nullptr;
-}
-
-scoped_refptr<base::SingleThreadTaskRunner>
-TestURLRequestContextGetter::GetNetworkTaskRunner() const {
-  return task_runner_;
-}
-
 }  // namespace payments
diff --git a/components/payments/core/test_payment_request_delegate.h b/components/payments/core/test_payment_request_delegate.h
index e7bb002..6c07f63 100644
--- a/components/payments/core/test_payment_request_delegate.h
+++ b/components/payments/core/test_payment_request_delegate.h
@@ -15,6 +15,9 @@
 #include "components/autofill/core/browser/test_autofill_client.h"
 #include "components/payments/core/payment_request_delegate.h"
 #include "net/url_request/url_request_test_util.h"
+#include "services/network/public/cpp/shared_url_loader_factory.h"
+#include "services/network/public/cpp/weak_wrapper_shared_url_loader_factory.h"
+#include "services/network/test/test_url_loader_factory.h"
 
 namespace payments {
 
@@ -30,20 +33,6 @@
                        const std::string& real_pan) override;
 };
 
-class TestURLRequestContextGetter : public net::URLRequestContextGetter {
- public:
-  explicit TestURLRequestContextGetter(
-      scoped_refptr<base::SingleThreadTaskRunner> task_runner);
-  net::URLRequestContext* GetURLRequestContext() override;
-  scoped_refptr<base::SingleThreadTaskRunner> GetNetworkTaskRunner()
-      const override;
-
- private:
-  ~TestURLRequestContextGetter() override;
-
-  scoped_refptr<base::SingleThreadTaskRunner> task_runner_;
-};
-
 class TestPaymentRequestDelegate : public PaymentRequestDelegate {
  public:
   explicit TestPaymentRequestDelegate(
@@ -82,7 +71,8 @@
   std::string locale_;
   const GURL last_committed_url_;
   autofill::TestAddressNormalizer address_normalizer_;
-  scoped_refptr<TestURLRequestContextGetter> request_context_;
+  network::TestURLLoaderFactory test_url_loader_factory_;
+  scoped_refptr<network::SharedURLLoaderFactory> test_shared_loader_factory_;
   autofill::TestAutofillClient autofill_client_;
   autofill::payments::PaymentsClient payments_client_;
   autofill::payments::FullCardRequest full_card_request_;
diff --git a/components/translate/core/common/translate_metrics.cc b/components/translate/core/common/translate_metrics.cc
index db31f0e..9135737 100644
--- a/components/translate/core/common/translate_metrics.cc
+++ b/components/translate/core/common/translate_metrics.cc
@@ -18,7 +18,6 @@
 
 namespace metrics_internal {
 
-const char kRenderer4LanguageDetection[] = "Renderer4.LanguageDetection";
 const char kTranslateContentLanguage[] = "Translate.ContentLanguage";
 const char kTranslateHtmlLang[] = "Translate.HtmlLang";
 const char kTranslateLanguageVerification[] = "Translate.LanguageVerification";
@@ -102,11 +101,6 @@
                             SCHEME_MAX);
 }
 
-void ReportLanguageDetectionTime(base::TimeTicks begin, base::TimeTicks end) {
-  UMA_HISTOGRAM_MEDIUM_TIMES(metrics_internal::kRenderer4LanguageDetection,
-                             end - begin);
-}
-
 void ReportSimilarLanguageMatch(bool match) {
   UMA_HISTOGRAM_BOOLEAN(metrics_internal::kTranslateSimilarLanguageMatch,
                         match);
diff --git a/components/translate/core/common/translate_metrics.h b/components/translate/core/common/translate_metrics.h
index 5dfbfb26..8c42c1f4 100644
--- a/components/translate/core/common/translate_metrics.h
+++ b/components/translate/core/common/translate_metrics.h
@@ -16,7 +16,6 @@
 namespace metrics_internal {
 
 // Constant string values to indicate UMA names.
-extern const char kRenderer4LanguageDetection[];
 extern const char kTranslateContentLanguage[];
 extern const char kTranslateHtmlLang[];
 extern const char kTranslateLanguageVerification[];
@@ -92,9 +91,6 @@
 // Called when a translation is triggered.
 void ReportPageScheme(const std::string& scheme);
 
-// Called when CLD detects page language.
-void ReportLanguageDetectionTime(base::TimeTicks begin, base::TimeTicks end);
-
 // Called when CLD agreed on a language which is different, but in the similar
 // language list.
 void ReportSimilarLanguageMatch(bool match);
diff --git a/components/translate/core/common/translate_metrics_unittest.cc b/components/translate/core/common/translate_metrics_unittest.cc
index 6f5ae19..44d1acf 100644
--- a/components/translate/core/common/translate_metrics_unittest.cc
+++ b/components/translate/core/common/translate_metrics_unittest.cc
@@ -279,17 +279,6 @@
   EXPECT_EQ(1, recorder.GetCount(kFalse));
 }
 
-TEST(TranslateMetricsTest, ReportLanguageDetectionTime) {
-  MetricsRecorder recorder(
-      translate::metrics_internal::kRenderer4LanguageDetection);
-  recorder.CheckTotalCount(0);
-  TimeTicks begin = TimeTicks::Now();
-  TimeTicks end = begin + base::TimeDelta::FromMicroseconds(9009);
-  translate::ReportLanguageDetectionTime(begin, end);
-  recorder.CheckValueInLogs(9.009);
-  recorder.CheckTotalCount(1);
-}
-
 TEST(TranslateMetricsTest, ReportLanguageDetectionConflict) {
   MetricsRecorder recorder(
       translate::metrics_internal::kTranslateLanguageDetectionConflict);
diff --git a/components/translate/core/language_detection/language_detection_util.cc b/components/translate/core/language_detection/language_detection_util.cc
index ea481c36e..e4e59ed 100644
--- a/components/translate/core/language_detection/language_detection_util.cc
+++ b/components/translate/core/language_detection/language_detection_util.cc
@@ -15,7 +15,6 @@
 #include "base/strings/string_split.h"
 #include "base/strings/string_util.h"
 #include "base/strings/utf_string_conversions.h"
-#include "base/time/time.h"
 #include "components/translate/core/common/translate_constants.h"
 #include "components/translate/core/common/translate_metrics.h"
 #include "components/translate/core/common/translate_util.h"
@@ -152,7 +151,6 @@
                                   const base::string16& contents,
                                   std::string* cld_language_p,
                                   bool* is_cld_reliable_p) {
-  base::TimeTicks begin_time = base::TimeTicks::Now();
   bool is_cld_reliable;
   // Check if html lang attribute is valid.
   std::string modified_html_lang;
@@ -172,7 +170,6 @@
   }
 
   std::string cld_language = DetermineTextLanguage(contents, &is_cld_reliable);
-  translate::ReportLanguageDetectionTime(begin_time, base::TimeTicks::Now());
 
   if (cld_language_p != nullptr)
     *cld_language_p = cld_language;
diff --git a/content/browser/devtools/protocol/browser_handler.cc b/content/browser/devtools/protocol/browser_handler.cc
index 7e03482..618ca84 100644
--- a/content/browser/devtools/protocol/browser_handler.cc
+++ b/content/browser/devtools/protocol/browser_handler.cc
@@ -47,10 +47,14 @@
 namespace {
 
 // Converts an histogram.
-std::unique_ptr<Browser::Histogram> Convert(
-    const base::HistogramBase& in_histogram) {
-  const std::unique_ptr<const base::HistogramSamples> in_buckets =
-      in_histogram.SnapshotSamples();
+std::unique_ptr<Browser::Histogram> Convert(base::HistogramBase& in_histogram,
+                                            bool in_delta) {
+  std::unique_ptr<const base::HistogramSamples> in_buckets;
+  if (!in_delta) {
+    in_buckets = in_histogram.SnapshotSamples();
+  } else {
+    in_buckets = in_histogram.SnapshotDelta();
+  }
   DCHECK(in_buckets);
 
   auto out_buckets = std::make_unique<Array<Browser::Bucket>>();
@@ -81,16 +85,17 @@
 
 Response BrowserHandler::GetHistograms(
     const Maybe<std::string> in_query,
+    const Maybe<bool> in_delta,
     std::unique_ptr<Array<Browser::Histogram>>* const out_histograms) {
   // Convert histograms.
   DCHECK(out_histograms);
   *out_histograms = std::make_unique<Array<Browser::Histogram>>();
-  for (const base::HistogramBase* const h :
+  for (base::HistogramBase* const h :
        base::StatisticsRecorder::Sort(base::StatisticsRecorder::WithName(
            base::StatisticsRecorder::GetHistograms(),
            in_query.fromMaybe("")))) {
     DCHECK(h);
-    (*out_histograms)->addItem(Convert(*h));
+    (*out_histograms)->addItem(Convert(*h, in_delta.fromMaybe(false)));
   }
 
   return Response::OK();
@@ -98,16 +103,17 @@
 
 Response BrowserHandler::GetHistogram(
     const std::string& in_name,
+    const Maybe<bool> in_delta,
     std::unique_ptr<Browser::Histogram>* const out_histogram) {
   // Get histogram by name.
-  const base::HistogramBase* const in_histogram =
+  base::HistogramBase* const in_histogram =
       base::StatisticsRecorder::FindHistogram(in_name);
   if (!in_histogram)
     return Response::InvalidParams("Cannot find histogram: " + in_name);
 
   // Convert histogram.
   DCHECK(out_histogram);
-  *out_histogram = Convert(*in_histogram);
+  *out_histogram = Convert(*in_histogram, in_delta.fromMaybe(false));
 
   return Response::OK();
 }
diff --git a/content/browser/devtools/protocol/browser_handler.h b/content/browser/devtools/protocol/browser_handler.h
index b4f3e05..9ced024 100644
--- a/content/browser/devtools/protocol/browser_handler.h
+++ b/content/browser/devtools/protocol/browser_handler.h
@@ -28,10 +28,12 @@
 
   Response GetHistograms(
       Maybe<std::string> in_query,
+      Maybe<bool> in_delta,
       std::unique_ptr<Array<Browser::Histogram>>* histograms) override;
 
   Response GetHistogram(
       const std::string& in_name,
+      Maybe<bool> in_delta,
       std::unique_ptr<Browser::Histogram>* out_histogram) override;
 
   Response GetBrowserCommandLine(
diff --git a/content/browser/renderer_host/media/fake_video_capture_device_launcher.cc b/content/browser/renderer_host/media/fake_video_capture_device_launcher.cc
index 5874c90..cad6736 100644
--- a/content/browser/renderer_host/media/fake_video_capture_device_launcher.cc
+++ b/content/browser/renderer_host/media/fake_video_capture_device_launcher.cc
@@ -78,6 +78,7 @@
           std::make_unique<media::VideoCaptureBufferTrackerFactoryImpl>(),
           kMaxBufferCount));
   auto device_client = std::make_unique<media::VideoCaptureDeviceClient>(
+      media::VideoCaptureBufferType::kSharedMemory,
       std::make_unique<media::VideoFrameReceiverOnTaskRunner>(
           receiver, base::ThreadTaskRunnerHandle::Get()),
       std::move(buffer_pool), std::move(empty_jpeg_decoder_factory_cb));
diff --git a/content/browser/renderer_host/media/in_process_video_capture_device_launcher.cc b/content/browser/renderer_host/media/in_process_video_capture_device_launcher.cc
index 6c4ec35..77742e8 100644
--- a/content/browser/renderer_host/media/in_process_video_capture_device_launcher.cc
+++ b/content/browser/renderer_host/media/in_process_video_capture_device_launcher.cc
@@ -114,7 +114,8 @@
           &InProcessVideoCaptureDeviceLauncher::
               DoStartDeviceCaptureOnDeviceThread,
           base::Unretained(this), device_id, params,
-          CreateDeviceClient(kMaxNumberOfBuffers, std::move(receiver),
+          CreateDeviceClient(media::VideoCaptureBufferType::kSharedMemory,
+                             kMaxNumberOfBuffers, std::move(receiver),
                              std::move(receiver_on_io_thread)),
           std::move(after_start_capture_callback));
       break;
@@ -181,7 +182,8 @@
           &InProcessVideoCaptureDeviceLauncher::
               DoStartDesktopCaptureOnDeviceThread,
           base::Unretained(this), desktop_id, params,
-          CreateDeviceClient(kMaxNumberOfBuffers, std::move(receiver),
+          CreateDeviceClient(media::VideoCaptureBufferType::kSharedMemory,
+                             kMaxNumberOfBuffers, std::move(receiver),
                              std::move(receiver_on_io_thread)),
           std::move(after_start_capture_callback));
       break;
@@ -207,6 +209,7 @@
 
 std::unique_ptr<media::VideoCaptureDeviceClient>
 InProcessVideoCaptureDeviceLauncher::CreateDeviceClient(
+    media::VideoCaptureBufferType requested_buffer_type,
     int buffer_pool_max_buffer_count,
     std::unique_ptr<media::VideoFrameReceiver> receiver,
     base::WeakPtr<media::VideoFrameReceiver> receiver_on_io_thread) {
@@ -218,7 +221,7 @@
           buffer_pool_max_buffer_count);
 
   return std::make_unique<media::VideoCaptureDeviceClient>(
-      std::move(receiver), std::move(buffer_pool),
+      requested_buffer_type, std::move(receiver), std::move(buffer_pool),
       base::BindRepeating(
           &CreateGpuJpegDecoder,
           base::BindRepeating(&media::VideoFrameReceiver::OnFrameReadyInBuffer,
diff --git a/content/browser/renderer_host/media/in_process_video_capture_device_launcher.h b/content/browser/renderer_host/media/in_process_video_capture_device_launcher.h
index b2919080..ce862c5 100644
--- a/content/browser/renderer_host/media/in_process_video_capture_device_launcher.h
+++ b/content/browser/renderer_host/media/in_process_video_capture_device_launcher.h
@@ -51,6 +51,7 @@
   };
 
   std::unique_ptr<media::VideoCaptureDeviceClient> CreateDeviceClient(
+      media::VideoCaptureBufferType requested_buffer_type,
       int buffer_pool_max_buffer_count,
       std::unique_ptr<media::VideoFrameReceiver> receiver,
       base::WeakPtr<media::VideoFrameReceiver> receiver_on_io_thread);
diff --git a/content/browser/renderer_host/media/video_capture_controller_unittest.cc b/content/browser/renderer_host/media/video_capture_controller_unittest.cc
index 75e032b..a9ae1331 100644
--- a/content/browser/renderer_host/media/video_capture_controller_unittest.cc
+++ b/content/browser/renderer_host/media/video_capture_controller_unittest.cc
@@ -154,6 +154,7 @@
         std::make_unique<media::VideoCaptureBufferTrackerFactoryImpl>(),
         kPoolSize);
     device_client_.reset(new media::VideoCaptureDeviceClient(
+        media::VideoCaptureBufferType::kSharedMemory,
         std::make_unique<media::VideoFrameReceiverOnTaskRunner>(
             controller_->GetWeakPtrForIOThread(),
             BrowserThread::GetTaskRunnerForThread(BrowserThread::IO)),
diff --git a/content/browser/webauth/webauth_browsertest.cc b/content/browser/webauth/webauth_browsertest.cc
index 3d052c0..91aa5f7 100644
--- a/content/browser/webauth/webauth_browsertest.cc
+++ b/content/browser/webauth/webauth_browsertest.cc
@@ -180,14 +180,16 @@
 // Cancels all navigations in a WebContents while in scope.
 class ScopedNavigationCancellingThrottleInstaller : public WebContentsObserver {
  public:
-  ScopedNavigationCancellingThrottleInstaller(WebContents* web_contents)
+  explicit ScopedNavigationCancellingThrottleInstaller(
+      WebContents* web_contents)
       : WebContentsObserver(web_contents) {}
   ~ScopedNavigationCancellingThrottleInstaller() override = default;
 
  protected:
   class CancellingThrottle : public NavigationThrottle {
    public:
-    CancellingThrottle(NavigationHandle* handle) : NavigationThrottle(handle) {}
+    explicit CancellingThrottle(NavigationHandle* handle)
+        : NavigationThrottle(handle) {}
     ~CancellingThrottle() override = default;
 
    protected:
@@ -249,9 +251,9 @@
   }
 
  private:
-  DISALLOW_COPY_AND_ASSIGN(WebAuthBrowserTestClientDelegate);
-
   WebAuthBrowserTestState* const test_state_;
+
+  DISALLOW_COPY_AND_ASSIGN(WebAuthBrowserTestClientDelegate);
 };
 
 // Implements ContentBrowserClient and allows webauthn-related calls to be
@@ -288,9 +290,9 @@
   }
 
  private:
-  DISALLOW_COPY_AND_ASSIGN(WebAuthBrowserTestContentBrowserClient);
-
   WebAuthBrowserTestState* const test_state_;
+
+  DISALLOW_COPY_AND_ASSIGN(WebAuthBrowserTestContentBrowserClient);
 };
 
 // Test fixture base class for common tasks.
@@ -408,9 +410,8 @@
 
     auto descriptor = webauth::mojom::PublicKeyCredentialDescriptor::New(
         webauth::mojom::PublicKeyCredentialType::PUBLIC_KEY,
-        std::vector<uint8_t>(
-            std::begin(device::test_data::kTestGetAssertionCredentialId),
-            std::end(device::test_data::kTestGetAssertionCredentialId)),
+        device::fido_parsing_utils::Materialize(
+            device::test_data::kTestGetAssertionCredentialId),
         transports);
     credentials.push_back(std::move(descriptor));
 
@@ -679,6 +680,9 @@
   DISALLOW_COPY_AND_ASSIGN(WebAuthJavascriptClientBrowserTest);
 };
 
+constexpr device::ProtocolVersion kAllProtocols[] = {
+    device::ProtocolVersion::kCtap, device::ProtocolVersion::kU2f};
+
 // Tests that when navigator.credentials.create() is called with an invalid
 // relying party id, we get a SecurityError.
 IN_PROC_BROWSER_TEST_F(WebAuthJavascriptClientBrowserTest,
@@ -698,74 +702,100 @@
 // verification required, request times out.
 IN_PROC_BROWSER_TEST_F(WebAuthJavascriptClientBrowserTest,
                        CreatePublicKeyCredentialWithUserVerification) {
-  CreateParameters parameters;
-  parameters.user_verification = kRequiredVerification;
-  std::string result;
-  ASSERT_TRUE(content::ExecuteScriptAndExtractString(
-      shell()->web_contents()->GetMainFrame(),
-      BuildCreateCallWithParameters(parameters), &result));
-  ASSERT_EQ(kTimeoutErrorMessage, result);
+  for (const auto protocol : kAllProtocols) {
+    device::test::ScopedVirtualFidoDevice virtual_device;
+    virtual_device.SetSupportedProtocol(protocol);
+
+    CreateParameters parameters;
+    parameters.user_verification = kRequiredVerification;
+    std::string result;
+    ASSERT_TRUE(content::ExecuteScriptAndExtractString(
+        shell()->web_contents()->GetMainFrame(),
+        BuildCreateCallWithParameters(parameters), &result));
+    ASSERT_EQ(kTimeoutErrorMessage, result);
+  }
 }
 
 // Tests that when navigator.credentials.create() is called with resident key
 // required, request times out.
 IN_PROC_BROWSER_TEST_F(WebAuthJavascriptClientBrowserTest,
                        CreatePublicKeyCredentialWithResidentKeyRequired) {
-  CreateParameters parameters;
-  parameters.require_resident_key = true;
-  std::string result;
-  ASSERT_TRUE(content::ExecuteScriptAndExtractString(
-      shell()->web_contents()->GetMainFrame(),
-      BuildCreateCallWithParameters(parameters), &result));
+  for (const auto protocol : kAllProtocols) {
+    device::test::ScopedVirtualFidoDevice virtual_device;
+    virtual_device.SetSupportedProtocol(protocol);
 
-  ASSERT_EQ(kTimeoutErrorMessage, result);
+    CreateParameters parameters;
+    parameters.require_resident_key = true;
+    std::string result;
+    ASSERT_TRUE(content::ExecuteScriptAndExtractString(
+        shell()->web_contents()->GetMainFrame(),
+        BuildCreateCallWithParameters(parameters), &result));
+
+    ASSERT_EQ(kTimeoutErrorMessage, result);
+  }
 }
 
 // Tests that when navigator.credentials.create() is called with an
 // unsupported algorithm, request times out.
 IN_PROC_BROWSER_TEST_F(WebAuthJavascriptClientBrowserTest,
                        CreatePublicKeyCredentialAlgorithmNotSupported) {
-  CreateParameters parameters;
-  parameters.algorithm_identifier = "123";
-  std::string result;
-  ASSERT_TRUE(content::ExecuteScriptAndExtractString(
-      shell()->web_contents()->GetMainFrame(),
-      BuildCreateCallWithParameters(parameters), &result));
+  for (const auto protocol : kAllProtocols) {
+    device::test::ScopedVirtualFidoDevice virtual_device;
+    virtual_device.SetSupportedProtocol(protocol);
 
-  ASSERT_EQ(kTimeoutErrorMessage, result);
+    CreateParameters parameters;
+    parameters.algorithm_identifier = "123";
+    std::string result;
+    ASSERT_TRUE(content::ExecuteScriptAndExtractString(
+        shell()->web_contents()->GetMainFrame(),
+        BuildCreateCallWithParameters(parameters), &result));
+
+    ASSERT_EQ(kTimeoutErrorMessage, result);
+  }
 }
 
 // Tests that when navigator.credentials.create() is called with a
 // platform authenticator requested, request times out.
 IN_PROC_BROWSER_TEST_F(WebAuthJavascriptClientBrowserTest,
                        CreatePublicKeyCredentialPlatformAuthenticator) {
-  CreateParameters parameters;
-  parameters.authenticator_attachment = kPlatform;
-  std::string result;
-  ASSERT_TRUE(content::ExecuteScriptAndExtractString(
-      shell()->web_contents()->GetMainFrame(),
-      BuildCreateCallWithParameters(parameters), &result));
+  for (const auto protocol : kAllProtocols) {
+    device::test::ScopedVirtualFidoDevice virtual_device;
+    virtual_device.SetSupportedProtocol(protocol);
 
-  ASSERT_EQ(kTimeoutErrorMessage, result);
+    CreateParameters parameters;
+    parameters.authenticator_attachment = kPlatform;
+    std::string result;
+    ASSERT_TRUE(content::ExecuteScriptAndExtractString(
+        shell()->web_contents()->GetMainFrame(),
+        BuildCreateCallWithParameters(parameters), &result));
+
+    ASSERT_EQ(kTimeoutErrorMessage, result);
+  }
 }
 
 // Tests that when navigator.credentials.get() is called with user verification
 // required, we get a NotSupportedError.
 IN_PROC_BROWSER_TEST_F(WebAuthJavascriptClientBrowserTest,
                        GetPublicKeyCredentialUserVerification) {
-  GetParameters parameters;
-  parameters.user_verification = "required";
-  std::string result;
-  ASSERT_TRUE(content::ExecuteScriptAndExtractString(
-      shell()->web_contents()->GetMainFrame(),
-      BuildGetCallWithParameters(parameters), &result));
-  ASSERT_EQ(kTimeoutErrorMessage, result);
+  for (const auto protocol : kAllProtocols) {
+    device::test::ScopedVirtualFidoDevice virtual_device;
+    virtual_device.SetSupportedProtocol(protocol);
+
+    GetParameters parameters;
+    parameters.user_verification = "required";
+    std::string result;
+    ASSERT_TRUE(content::ExecuteScriptAndExtractString(
+        shell()->web_contents()->GetMainFrame(),
+        BuildGetCallWithParameters(parameters), &result));
+    ASSERT_EQ(kTimeoutErrorMessage, result);
+  }
 }
 
 // Tests that when navigator.credentials.get() is called with an empty
 // allowCredentials list, we get a NotSupportedError.
 IN_PROC_BROWSER_TEST_F(WebAuthJavascriptClientBrowserTest,
                        GetPublicKeyCredentialEmptyAllowCredentialsList) {
+  device::test::ScopedVirtualFidoDevice virtual_device;
   GetParameters parameters;
   parameters.allow_credentials = "";
   std::string result;
@@ -977,55 +1007,83 @@
   DISALLOW_COPY_AND_ASSIGN(WebAuthBrowserCtapTest);
 };
 
-// TODO(hongjunchoi): Implement VirtualCtap2Device to replace mocking.
-// See: https://crbugs.com/829413
-IN_PROC_BROWSER_TEST_F(WebAuthBrowserCtapTest, TestCtapMakeCredential) {
-  device::test::ScopedFakeFidoDiscoveryFactory discovery_factory;
-  auto* fake_hid_discovery = discovery_factory.ForgeNextHidDiscovery();
+IN_PROC_BROWSER_TEST_F(WebAuthBrowserCtapTest, TestMakeCredential) {
+  for (const auto protocol : kAllProtocols) {
+    device::test::ScopedVirtualFidoDevice virtual_device;
+    virtual_device.SetSupportedProtocol(protocol);
 
-  TestCreateCallbackReceiver create_callback_receiver;
-  authenticator()->MakeCredential(BuildBasicCreateOptions(),
-                                  create_callback_receiver.callback());
+    TestCreateCallbackReceiver create_callback_receiver;
+    authenticator()->MakeCredential(BuildBasicCreateOptions(),
+                                    create_callback_receiver.callback());
 
-  fake_hid_discovery->WaitForCallToStartAndSimulateSuccess();
-  auto device = std::make_unique<device::MockFidoDevice>();
-  EXPECT_CALL(*device, GetId()).WillRepeatedly(testing::Return("device0"));
-  device->ExpectCtap2CommandAndRespondWith(
-      device::CtapRequestCommand::kAuthenticatorGetInfo,
-      device::test_data::kTestAuthenticatorGetInfoResponse);
-  device->ExpectCtap2CommandAndRespondWith(
-      device::CtapRequestCommand::kAuthenticatorMakeCredential,
-      device::test_data::kTestMakeCredentialResponse);
-
-  fake_hid_discovery->AddDevice(std::move(device));
-
-  create_callback_receiver.WaitForCallback();
-  EXPECT_EQ(AuthenticatorStatus::SUCCESS, create_callback_receiver.status());
+    create_callback_receiver.WaitForCallback();
+    EXPECT_EQ(AuthenticatorStatus::SUCCESS, create_callback_receiver.status());
+  }
 }
 
-IN_PROC_BROWSER_TEST_F(WebAuthBrowserCtapTest, TestCtapGetAssertion) {
-  device::test::ScopedFakeFidoDiscoveryFactory discovery_factory;
-  auto* fake_hid_discovery = discovery_factory.ForgeNextHidDiscovery();
+IN_PROC_BROWSER_TEST_F(WebAuthBrowserCtapTest,
+                       TestMakeCredentialWithDuplicateKeyHandle) {
+  for (const auto protocol : kAllProtocols) {
+    device::test::ScopedVirtualFidoDevice virtual_device;
+    virtual_device.SetSupportedProtocol(protocol);
+    auto make_credential_request = BuildBasicCreateOptions();
+    auto excluded_credential =
+        webauth::mojom::PublicKeyCredentialDescriptor::New(
+            webauth::mojom::PublicKeyCredentialType::PUBLIC_KEY,
+            device::fido_parsing_utils::Materialize(
+                device::test_data::kCtap2MakeCredentialCredentialId),
+            std::vector<webauth::mojom::AuthenticatorTransport>{
+                webauth::mojom::AuthenticatorTransport::USB});
+    make_credential_request->exclude_credentials.push_back(
+        std::move(excluded_credential));
 
-  TestGetCallbackReceiver get_callback_receiver;
-  auto get_assertion_request_params = BuildBasicGetOptions();
-  authenticator()->GetAssertion(std::move(get_assertion_request_params),
-                                get_callback_receiver.callback());
+    ASSERT_TRUE(virtual_device.mutable_state()->InjectRegistration(
+        device::fido_parsing_utils::Materialize(
+            device::test_data::kCtap2MakeCredentialCredentialId),
+        make_credential_request->relying_party->id));
 
-  fake_hid_discovery->WaitForCallToStartAndSimulateSuccess();
-  auto device = std::make_unique<device::MockFidoDevice>();
-  EXPECT_CALL(*device, GetId()).WillRepeatedly(testing::Return("device0"));
-  device->ExpectCtap2CommandAndRespondWith(
-      device::CtapRequestCommand::kAuthenticatorGetInfo,
-      device::test_data::kTestAuthenticatorGetInfoResponse);
-  device->ExpectCtap2CommandAndRespondWith(
-      device::CtapRequestCommand::kAuthenticatorGetAssertion,
-      device::test_data::kTestGetAssertionResponse);
+    TestCreateCallbackReceiver create_callback_receiver;
+    authenticator()->MakeCredential(std::move(make_credential_request),
+                                    create_callback_receiver.callback());
 
-  fake_hid_discovery->AddDevice(std::move(device));
+    create_callback_receiver.WaitForCallback();
+    EXPECT_EQ(AuthenticatorStatus::CREDENTIAL_EXCLUDED,
+              create_callback_receiver.status());
+  }
+}
 
-  get_callback_receiver.WaitForCallback();
-  EXPECT_EQ(AuthenticatorStatus::SUCCESS, get_callback_receiver.status());
+IN_PROC_BROWSER_TEST_F(WebAuthBrowserCtapTest, TestGetAssertion) {
+  for (const auto protocol : kAllProtocols) {
+    device::test::ScopedVirtualFidoDevice virtual_device;
+    virtual_device.SetSupportedProtocol(protocol);
+    auto get_assertion_request_params = BuildBasicGetOptions();
+    ASSERT_TRUE(virtual_device.mutable_state()->InjectRegistration(
+        device::fido_parsing_utils::Materialize(
+            device::test_data::kTestGetAssertionCredentialId),
+        get_assertion_request_params->relying_party_id));
+
+    TestGetCallbackReceiver get_callback_receiver;
+    authenticator()->GetAssertion(std::move(get_assertion_request_params),
+                                  get_callback_receiver.callback());
+    get_callback_receiver.WaitForCallback();
+    EXPECT_EQ(AuthenticatorStatus::SUCCESS, get_callback_receiver.status());
+  }
+}
+
+IN_PROC_BROWSER_TEST_F(WebAuthBrowserCtapTest,
+                       TestGetAssertionWithNoMatchingKeyHandles) {
+  for (const auto protocol : kAllProtocols) {
+    device::test::ScopedVirtualFidoDevice virtual_device;
+    virtual_device.SetSupportedProtocol(protocol);
+    auto get_assertion_request_params = BuildBasicGetOptions();
+
+    TestGetCallbackReceiver get_callback_receiver;
+    authenticator()->GetAssertion(std::move(get_assertion_request_params),
+                                  get_callback_receiver.callback());
+    get_callback_receiver.WaitForCallback();
+    EXPECT_EQ(AuthenticatorStatus::CREDENTIAL_NOT_RECOGNIZED,
+              get_callback_receiver.status());
+  }
 }
 
 }  // namespace
diff --git a/content/renderer/media/video_capture_impl.cc b/content/renderer/media/video_capture_impl.cc
index d5c5d83..edf50675 100644
--- a/content/renderer/media/video_capture_impl.cc
+++ b/content/renderer/media/video_capture_impl.cc
@@ -41,6 +41,9 @@
         InitializeFromSharedMemory(
             std::move(buffer_handle->get_shared_buffer_handle()));
         break;
+      case VideoFrameBufferHandleType::SHARED_MEMORY_VIA_RAW_FILE_DESCRIPTOR:
+        NOTREACHED();
+        break;
       case VideoFrameBufferHandleType::MAILBOX_HANDLES:
         InitializeFromMailbox(std::move(buffer_handle->get_mailbox_handles()));
         break;
@@ -346,6 +349,9 @@
           buffer_context->shared_memory()->handle(),
           0 /* shared_memory_offset */, info->timestamp);
       break;
+    case VideoFrameBufferHandleType::SHARED_MEMORY_VIA_RAW_FILE_DESCRIPTOR:
+      NOTREACHED();
+      break;
     case VideoFrameBufferHandleType::MAILBOX_HANDLES:
       gpu::MailboxHolder mailbox_holder_array[media::VideoFrame::kMaxPlanes];
       CHECK_EQ(media::VideoFrame::kMaxPlanes,
diff --git a/content/renderer/service_worker/service_worker_context_client_unittest.cc b/content/renderer/service_worker/service_worker_context_client_unittest.cc
index 94e0b1f..c9cebe0 100644
--- a/content/renderer/service_worker/service_worker_context_client_unittest.cc
+++ b/content/renderer/service_worker/service_worker_context_client_unittest.cc
@@ -245,6 +245,7 @@
     ServiceWorkerContextClient::ResetThreadSpecificInstanceForTesting();
     // Unregister this thread from worker threads.
     WorkerThreadRegistry::Instance()->WillStopCurrentWorkerThread();
+    task_runner_->RunUntilIdle();
   }
 
   void EnableServicification() {
diff --git a/device/fido/get_assertion_task_unittest.cc b/device/fido/get_assertion_task_unittest.cc
index fc69dd5..d01b4da5 100644
--- a/device/fido/get_assertion_task_unittest.cc
+++ b/device/fido/get_assertion_task_unittest.cc
@@ -6,12 +6,14 @@
 
 #include <iterator>
 #include <memory>
+#include <string>
 #include <utility>
 #include <vector>
 
 #include "base/bind.h"
 #include "base/test/scoped_feature_list.h"
 #include "base/test/scoped_task_environment.h"
+#include "crypto/ec_private_key.h"
 #include "device/base/features.h"
 #include "device/fido/authenticator_get_assertion_response.h"
 #include "device/fido/ctap_get_assertion_request.h"
@@ -20,6 +22,7 @@
 #include "device/fido/fido_test_data.h"
 #include "device/fido/mock_fido_device.h"
 #include "device/fido/test_callback_receiver.h"
+#include "device/fido/virtual_ctap2_device.h"
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
@@ -113,6 +116,51 @@
   EXPECT_FALSE(device->device_info());
 }
 
+TEST_F(FidoGetAssertionTaskTest, TestSignSuccessWithFake) {
+  auto private_key = crypto::ECPrivateKey::Create();
+  std::string public_key;
+  private_key->ExportRawPublicKey(&public_key);
+  auto hash = fido_parsing_utils::CreateSHA256Hash(public_key);
+  std::vector<uint8_t> key_handle(hash.begin(), hash.end());
+  CtapGetAssertionRequest request_param(test_data::kRelyingPartyId,
+                                        test_data::kClientDataHash);
+  request_param.SetAllowList({{CredentialType::kPublicKey, key_handle}});
+
+  auto device = std::make_unique<VirtualCtap2Device>();
+  device->mutable_state()->registrations.emplace(
+      key_handle,
+      VirtualFidoDevice::RegistrationData(
+          std::move(private_key),
+          fido_parsing_utils::CreateSHA256Hash(test_data::kRelyingPartyId),
+          42 /* counter */));
+
+  auto task = std::make_unique<GetAssertionTask>(
+      device.get(), std::move(request_param),
+      get_assertion_callback_receiver().callback());
+
+  get_assertion_callback_receiver().WaitForCallback();
+  EXPECT_EQ(CtapDeviceResponseCode::kSuccess,
+            get_assertion_callback_receiver().status());
+
+  // Just a sanity check, we don't verify the actual signature.
+  ASSERT_GE(32u + 1u + 4u + 8u,  // Minimal ECDSA signature is 8 bytes
+            get_assertion_callback_receiver()
+                .value()
+                ->auth_data()
+                .SerializeToByteArray()
+                .size());
+  EXPECT_EQ(0x01,
+            get_assertion_callback_receiver()
+                .value()
+                ->auth_data()
+                .SerializeToByteArray()[32]);  // UP flag
+  // Counter is incremented for every sign request.
+  EXPECT_EQ(43, get_assertion_callback_receiver()
+                    .value()
+                    ->auth_data()
+                    .SerializeToByteArray()[36]);  // counter
+}
+
 TEST_F(FidoGetAssertionTaskTest, TestU2fSignWithoutFlag) {
   RemoveCtapFlag();
   auto device = std::make_unique<MockFidoDevice>();
diff --git a/device/fido/scoped_virtual_fido_device.cc b/device/fido/scoped_virtual_fido_device.cc
index 782c8d67..4449bc152 100644
--- a/device/fido/scoped_virtual_fido_device.cc
+++ b/device/fido/scoped_virtual_fido_device.cc
@@ -12,6 +12,7 @@
 #include "base/macros.h"
 #include "base/memory/weak_ptr.h"
 #include "base/threading/sequenced_task_runner_handle.h"
+#include "device/fido/virtual_ctap2_device.h"
 #include "device/fido/virtual_u2f_device.h"
 
 namespace device {
@@ -23,14 +24,21 @@
       public base::SupportsWeakPtr<VirtualFidoDeviceDiscovery> {
  public:
   explicit VirtualFidoDeviceDiscovery(
-      scoped_refptr<VirtualFidoDevice::State> state)
+      scoped_refptr<VirtualFidoDevice::State> state,
+      ProtocolVersion supported_protocol)
       : FidoDiscovery(FidoTransportProtocol::kUsbHumanInterfaceDevice),
-        state_(std::move(state)) {}
+        state_(std::move(state)),
+        supported_protocol_(supported_protocol) {}
   ~VirtualFidoDeviceDiscovery() override = default;
 
  protected:
   void StartInternal() override {
-    auto device = std::make_unique<VirtualU2fDevice>(state_);
+    std::unique_ptr<FidoDevice> device;
+    if (supported_protocol_ == ProtocolVersion::kCtap)
+      device = std::make_unique<VirtualCtap2Device>(state_);
+    else
+      device = std::make_unique<VirtualU2fDevice>(state_);
+
     AddDevice(std::move(device));
     base::SequencedTaskRunnerHandle::Get()->PostTask(
         FROM_HERE,
@@ -40,6 +48,7 @@
 
  private:
   scoped_refptr<VirtualFidoDevice::State> state_;
+  ProtocolVersion supported_protocol_;
   DISALLOW_COPY_AND_ASSIGN(VirtualFidoDeviceDiscovery);
 };
 
@@ -47,6 +56,11 @@
     : state_(new VirtualFidoDevice::State) {}
 ScopedVirtualFidoDevice::~ScopedVirtualFidoDevice() = default;
 
+void ScopedVirtualFidoDevice::SetSupportedProtocol(
+    ProtocolVersion supported_protocol) {
+  supported_protocol_ = ProtocolVersion::kCtap;
+}
+
 VirtualFidoDevice::State* ScopedVirtualFidoDevice::mutable_state() {
   return state_.get();
 }
@@ -57,7 +71,8 @@
   if (transport != FidoTransportProtocol::kUsbHumanInterfaceDevice) {
     return nullptr;
   }
-  return std::make_unique<VirtualFidoDeviceDiscovery>(state_);
+  return std::make_unique<VirtualFidoDeviceDiscovery>(state_,
+                                                      supported_protocol_);
 }
 
 }  // namespace test
diff --git a/device/fido/scoped_virtual_fido_device.h b/device/fido/scoped_virtual_fido_device.h
index a9c8d8583..4c63e52 100644
--- a/device/fido/scoped_virtual_fido_device.h
+++ b/device/fido/scoped_virtual_fido_device.h
@@ -8,6 +8,7 @@
 #include <memory>
 
 #include "base/macros.h"
+#include "device/fido/fido_constants.h"
 #include "device/fido/fido_discovery.h"
 #include "device/fido/virtual_fido_device.h"
 
@@ -23,6 +24,7 @@
   ScopedVirtualFidoDevice();
   ~ScopedVirtualFidoDevice() override;
 
+  void SetSupportedProtocol(ProtocolVersion supported_protocol);
   VirtualFidoDevice::State* mutable_state();
 
  protected:
@@ -31,6 +33,7 @@
       ::service_manager::Connector* connector) override;
 
  private:
+  ProtocolVersion supported_protocol_ = ProtocolVersion::kU2f;
   scoped_refptr<VirtualFidoDevice::State> state_;
   DISALLOW_COPY_AND_ASSIGN(ScopedVirtualFidoDevice);
 };
diff --git a/device/fido/virtual_ctap2_device.cc b/device/fido/virtual_ctap2_device.cc
index da36e42b..60d45790 100644
--- a/device/fido/virtual_ctap2_device.cc
+++ b/device/fido/virtual_ctap2_device.cc
@@ -9,11 +9,14 @@
 #include <utility>
 
 #include "base/bind.h"
+#include "base/logging.h"
 #include "base/numerics/safe_conversions.h"
 #include "base/threading/thread_task_runner_handle.h"
 #include "components/cbor/cbor_writer.h"
 #include "crypto/ec_private_key.h"
+#include "device/fido/authenticator_get_assertion_response.h"
 #include "device/fido/authenticator_make_credential_response.h"
+#include "device/fido/ctap_get_assertion_request.h"
 #include "device/fido/ctap_make_credential_request.h"
 #include "device/fido/ec_public_key.h"
 #include "device/fido/fido_constants.h"
@@ -57,6 +60,18 @@
                  kSupportedAndConfigured;
 }
 
+bool AreGetAssertionOptionsValid(const AuthenticatorSupportedOptions& options,
+                                 const CtapGetAssertionRequest& request) {
+  if (request.user_presence_required() && !options.user_presence_required())
+    return false;
+
+  return request.user_verification() !=
+             UserVerificationRequirement::kRequired ||
+         options.user_verification_availability() ==
+             AuthenticatorSupportedOptions::UserVerificationAvailability::
+                 kSupportedAndConfigured;
+}
+
 // Checks that whether the received MakeCredential request includes EA256
 // algorithm in publicKeyCredParam.
 bool AreMakeCredentialParamsValid(const CtapMakeCredentialRequest& request) {
@@ -113,6 +128,20 @@
   return GetSerializedCtapDeviceResponse(make_credential_response);
 }
 
+std::vector<uint8_t> ConstructGetAssertionResponse(
+    AuthenticatorData authenticator_data,
+    base::span<const uint8_t> signature,
+    base::span<const uint8_t> key_handle) {
+  AuthenticatorGetAssertionResponse response(
+      std::move(authenticator_data),
+      fido_parsing_utils::Materialize(signature));
+
+  response.SetCredential({CredentialType::kPublicKey,
+                          fido_parsing_utils::Materialize(key_handle)});
+  response.SetNumCredentials(1);
+  return GetSerializedCtapDeviceResponse(response);
+}
+
 }  // namespace
 
 VirtualCtap2Device::VirtualCtap2Device()
@@ -158,6 +187,9 @@
     case CtapRequestCommand::kAuthenticatorMakeCredential:
       response_code = OnMakeCredential(request_bytes, &response_data);
       break;
+    case CtapRequestCommand::kAuthenticatorGetAssertion:
+      response_code = OnGetAssertion(request_bytes, &response_data);
+      break;
     default:
       break;
   }
@@ -180,17 +212,23 @@
     base::span<const uint8_t> request_bytes,
     std::vector<uint8_t>* response) {
   auto request = ParseCtapMakeCredentialRequest(request_bytes);
-  if (!request)
+  if (!request) {
+    DLOG(ERROR) << "Incorrectly formatted MakeCredential request.";
     return CtapDeviceResponseCode::kCtap2ErrOther;
+  }
 
   if (!AreMakeCredentialOptionsValid(device_info_.options(), *request) ||
       !AreMakeCredentialParamsValid(*request)) {
+    DLOG(ERROR) << "Virtual CTAP2 device does not support options required by "
+                   "the request.";
     return CtapDeviceResponseCode::kCtap2ErrOther;
   }
 
   // Client pin is not supported.
-  if (request->pin_auth())
+  if (request->pin_auth()) {
+    DLOG(ERROR) << "Virtual CTAP2 device does not support client pin.";
     return CtapDeviceResponseCode::kCtap2ErrPinInvalid;
+  }
 
   // Check for already registered credentials.
   const auto rp_id_hash =
@@ -213,13 +251,14 @@
   // Our key handles are simple hashes of the public key.
   auto hash = fido_parsing_utils::CreateSHA256Hash(public_key);
   std::vector<uint8_t> key_handle(hash.begin(), hash.end());
+  std::array<uint8_t, 2> sha256_length = {0, crypto::kSHA256Length};
 
   AttestedCredentialData attested_credential_data(
-      kDeviceAaguid, {{0, crypto::kSHA256Length}}, key_handle,
+      kDeviceAaguid, sha256_length, key_handle,
       ConstructECPublicKey(public_key));
 
   auto authenticator_data = ConstructAuthenticatorData(
-      rp_id_hash, std::move(attested_credential_data));
+      rp_id_hash, 01ul, std::move(attested_credential_data));
   auto sign_buffer =
       ConstructSignatureBuffer(authenticator_data, request->client_data_hash());
 
@@ -234,8 +273,10 @@
 
   auto attestation_cert = GenerateAttestationCertificate(
       false /* individual_attestation_requested */);
-  if (!attestation_cert)
+  if (!attestation_cert) {
+    DLOG(ERROR) << "Failed to generate attestation certificate.";
     return CtapDeviceResponseCode::kCtap2ErrOther;
+  }
 
   *response = ConstructMakeCredentialResponse(std::move(*attestation_cert), sig,
                                               std::move(authenticator_data));
@@ -244,6 +285,66 @@
   return CtapDeviceResponseCode::kSuccess;
 }
 
+CtapDeviceResponseCode VirtualCtap2Device::OnGetAssertion(
+    base::span<const uint8_t> request_bytes,
+    std::vector<uint8_t>* response) {
+  auto request = ParseCtapGetAssertionRequest(request_bytes);
+  if (!request) {
+    DLOG(ERROR) << "Incorrectly formatted GetAssertion request.";
+    return CtapDeviceResponseCode::kCtap2ErrOther;
+  }
+
+  // Resident keys are not supported.
+  if (!request->allow_list() || request->allow_list()->empty()) {
+    DLOG(ERROR) << "Allowed credential list is empty, but Virtual CTAP2 device "
+                   "does not support resident keys.";
+    return CtapDeviceResponseCode::kCtap2ErrNoCredentials;
+  }
+
+  // Client pin option is not supported.
+  if (request->pin_auth()) {
+    DLOG(ERROR) << "Virtual CTAP2 device does not support client pin.";
+    return CtapDeviceResponseCode::kCtap2ErrOther;
+  }
+
+  if (!AreGetAssertionOptionsValid(device_info_.options(), *request)) {
+    DLOG(ERROR) << "Unsupported options required from the request.";
+    return CtapDeviceResponseCode::kCtap2ErrOther;
+  }
+
+  const auto rp_id_hash =
+      fido_parsing_utils::CreateSHA256Hash(request->rp_id());
+
+  RegistrationData* found_data = nullptr;
+  base::span<const uint8_t> credential_id;
+  for (const auto& allowed_credential : *request->allow_list()) {
+    if ((found_data =
+             FindRegistrationData(allowed_credential.id(), rp_id_hash))) {
+      credential_id = allowed_credential.id();
+      break;
+    }
+  }
+
+  if (!found_data)
+    return CtapDeviceResponseCode::kCtap2ErrNoCredentials;
+
+  found_data->counter++;
+  auto authenticator_data =
+      ConstructAuthenticatorData(rp_id_hash, found_data->counter);
+  auto signature_buffer =
+      ConstructSignatureBuffer(authenticator_data, request->client_data_hash());
+
+  // Sign with the private key of the received key handle.
+  std::vector<uint8_t> sig;
+  auto* private_key = found_data->private_key.get();
+  bool status = Sign(private_key, std::move(signature_buffer), &sig);
+  DCHECK(status);
+
+  *response = ConstructGetAssertionResponse(std::move(authenticator_data),
+                                            std::move(sig), credential_id);
+  return CtapDeviceResponseCode::kSuccess;
+}
+
 CtapDeviceResponseCode VirtualCtap2Device::OnAuthenticatorGetInfo(
     std::vector<uint8_t>* response) const {
   *response = EncodeToCBOR(device_info_);
@@ -252,24 +353,20 @@
 
 AuthenticatorData VirtualCtap2Device::ConstructAuthenticatorData(
     base::span<const uint8_t, kRpIdHashLength> rp_id_hash,
+    uint32_t current_signature_count,
     base::Optional<AttestedCredentialData> attested_credential_data) {
   uint8_t flag =
       base::strict_cast<uint8_t>(AuthenticatorData::Flag::kTestOfUserPresence);
   std::array<uint8_t, kSignCounterLength> signature_counter;
 
   // Constructing AuthenticatorData for registration operation.
-  if (attested_credential_data) {
+  if (attested_credential_data)
     flag |= base::strict_cast<uint8_t>(AuthenticatorData::Flag::kAttestation);
-    signature_counter = {{0x00, 0x00, 0x00, 0x01}};
-    // Constructing AuthenticatorData for sign operation.
-  } else {
-    auto* registration_data = FindRegistrationData(
-        attested_credential_data->credential_id(), rp_id_hash);
-    signature_counter[0] = (registration_data->counter >> 24) & 0xff;
-    signature_counter[1] = (registration_data->counter >> 16) & 0xff;
-    signature_counter[2] = (registration_data->counter >> 8) & 0xff;
-    signature_counter[3] = (registration_data->counter) & 0xff;
-  }
+
+  signature_counter[0] = (current_signature_count >> 24) & 0xff;
+  signature_counter[1] = (current_signature_count >> 16) & 0xff;
+  signature_counter[2] = (current_signature_count >> 8) & 0xff;
+  signature_counter[3] = (current_signature_count)&0xff;
 
   return AuthenticatorData(rp_id_hash, flag, signature_counter,
                            std::move(attested_credential_data));
diff --git a/device/fido/virtual_ctap2_device.h b/device/fido/virtual_ctap2_device.h
index 7808b85..fa84a9c 100644
--- a/device/fido/virtual_ctap2_device.h
+++ b/device/fido/virtual_ctap2_device.h
@@ -41,12 +41,17 @@
   CtapDeviceResponseCode OnMakeCredential(base::span<const uint8_t> request,
                                           std::vector<uint8_t>* response);
 
+  CtapDeviceResponseCode OnGetAssertion(base::span<const uint8_t> request,
+                                        std::vector<uint8_t>* response);
+
   CtapDeviceResponseCode OnAuthenticatorGetInfo(
       std::vector<uint8_t>* response) const;
 
   AuthenticatorData ConstructAuthenticatorData(
       base::span<const uint8_t, kRpIdHashLength> rp_id_hash,
-      base::Optional<AttestedCredentialData> attested_credential_data);
+      uint32_t current_signature_count,
+      base::Optional<AttestedCredentialData> attested_credential_data =
+          base::nullopt);
 
   AuthenticatorGetInfoResponse device_info_;
   base::WeakPtrFactory<FidoDevice> weak_factory_;
diff --git a/gpu/BUILD.gn b/gpu/BUILD.gn
index 571a90b..e926a3e 100644
--- a/gpu/BUILD.gn
+++ b/gpu/BUILD.gn
@@ -480,6 +480,7 @@
     "//testing/gtest",
     "//third_party/angle:translator",
     "//third_party/mesa:mesa_headers",
+    "//third_party/mesa:osmesa",
     "//ui/gfx",
     "//ui/gfx:test_support",
     "//ui/gfx/geometry",
@@ -525,6 +526,7 @@
   data_deps = [
     # Needed for isolate script to execute.
     "//testing:run_perf_test",
+    "//third_party/mesa:osmesa",
   ]
 
   # This target should not require the Chrome executable to run.
diff --git a/gpu/command_buffer/service/BUILD.gn b/gpu/command_buffer/service/BUILD.gn
index 8ecfe12..92ca326 100644
--- a/gpu/command_buffer/service/BUILD.gn
+++ b/gpu/command_buffer/service/BUILD.gn
@@ -191,6 +191,8 @@
     "raster_cmd_validation_implementation_autogen.h",
     "raster_decoder.cc",
     "raster_decoder.h",
+    "raster_decoder_context_state.cc",
+    "raster_decoder_context_state.h",
     "renderbuffer_manager.cc",
     "renderbuffer_manager.h",
     "sampler_manager.cc",
diff --git a/gpu/command_buffer/service/gl_utils.cc b/gpu/command_buffer/service/gl_utils.cc
index bcc15ed..8645fd94 100644
--- a/gpu/command_buffer/service/gl_utils.cc
+++ b/gpu/command_buffer/service/gl_utils.cc
@@ -4,6 +4,7 @@
 
 #include "gpu/command_buffer/service/gl_utils.h"
 
+#include <algorithm>
 #include <unordered_set>
 
 #include "base/metrics/histogram.h"
@@ -641,5 +642,101 @@
   return CopyTextureMethod::DRAW_AND_COPY;
 }
 
+bool ValidateCopyTextureCHROMIUMInternalFormats(const FeatureInfo* feature_info,
+                                                GLenum source_internal_format,
+                                                GLenum dest_internal_format,
+                                                std::string* output_error_msg) {
+  bool valid_dest_format = false;
+  // TODO(qiankun.miao@intel.com): ALPHA, LUMINANCE and LUMINANCE_ALPHA formats
+  // are not supported on GL core profile. See https://crbug.com/577144. Enable
+  // the workaround for glCopyTexImage and glCopyTexSubImage in
+  // gles2_cmd_copy_tex_image.cc for glCopyTextureCHROMIUM implementation.
+  switch (dest_internal_format) {
+    case GL_RGB:
+    case GL_RGBA:
+    case GL_RGB8:
+    case GL_RGBA8:
+      valid_dest_format = true;
+      break;
+    case GL_BGRA_EXT:
+    case GL_BGRA8_EXT:
+      valid_dest_format =
+          feature_info->feature_flags().ext_texture_format_bgra8888;
+      break;
+    case GL_SRGB_EXT:
+    case GL_SRGB_ALPHA_EXT:
+      valid_dest_format = feature_info->feature_flags().ext_srgb;
+      break;
+    case GL_R8:
+    case GL_R8UI:
+    case GL_RG8:
+    case GL_RG8UI:
+    case GL_SRGB8:
+    case GL_RGB565:
+    case GL_RGB8UI:
+    case GL_SRGB8_ALPHA8:
+    case GL_RGB5_A1:
+    case GL_RGBA4:
+    case GL_RGBA8UI:
+    case GL_RGB10_A2:
+      valid_dest_format = feature_info->IsWebGL2OrES3Context();
+      break;
+    case GL_RGB9_E5:
+    case GL_R16F:
+    case GL_R32F:
+    case GL_RG16F:
+    case GL_RG32F:
+    case GL_RGB16F:
+    case GL_RGBA16F:
+    case GL_R11F_G11F_B10F:
+      valid_dest_format = feature_info->ext_color_buffer_float_available();
+      break;
+    case GL_RGB32F:
+      valid_dest_format =
+          feature_info->ext_color_buffer_float_available() ||
+          feature_info->feature_flags().chromium_color_buffer_float_rgb;
+      break;
+    case GL_RGBA32F:
+      valid_dest_format =
+          feature_info->ext_color_buffer_float_available() ||
+          feature_info->feature_flags().chromium_color_buffer_float_rgba;
+      break;
+    case GL_ALPHA:
+    case GL_LUMINANCE:
+    case GL_LUMINANCE_ALPHA:
+      valid_dest_format = true;
+      break;
+    default:
+      valid_dest_format = false;
+      break;
+  }
+
+  // TODO(aleksandar.stojiljkovic): Use sized internal formats:
+  // https://crbug.com/628064
+  bool valid_source_format =
+      source_internal_format == GL_RED || source_internal_format == GL_ALPHA ||
+      source_internal_format == GL_RGB || source_internal_format == GL_RGBA ||
+      source_internal_format == GL_RGB8 || source_internal_format == GL_RGBA8 ||
+      source_internal_format == GL_LUMINANCE ||
+      source_internal_format == GL_LUMINANCE_ALPHA ||
+      source_internal_format == GL_BGRA_EXT ||
+      source_internal_format == GL_BGRA8_EXT ||
+      source_internal_format == GL_RGB_YCBCR_420V_CHROMIUM ||
+      source_internal_format == GL_RGB_YCBCR_422_CHROMIUM ||
+      source_internal_format == GL_R16_EXT;
+  if (!valid_source_format) {
+    *output_error_msg = "invalid source internal format " +
+                        GLES2Util::GetStringEnum(source_internal_format);
+    return false;
+  }
+  if (!valid_dest_format) {
+    *output_error_msg = "invalid dest internal format " +
+                        GLES2Util::GetStringEnum(dest_internal_format);
+    return false;
+  }
+
+  return true;
+}
+
 }  // namespace gles2
 }  // namespace gpu
diff --git a/gpu/command_buffer/service/gl_utils.h b/gpu/command_buffer/service/gl_utils.h
index c55f3d2..350ae6e7 100644
--- a/gpu/command_buffer/service/gl_utils.h
+++ b/gpu/command_buffer/service/gl_utils.h
@@ -8,6 +8,7 @@
 #ifndef GPU_COMMAND_BUFFER_SERVICE_GL_UTILS_H_
 #define GPU_COMMAND_BUFFER_SERVICE_GL_UTILS_H_
 
+#include <string>
 #include <vector>
 
 #include "build/build_config.h"
@@ -124,6 +125,10 @@
                                                bool unpremultiply_alpha,
                                                bool dither);
 
+bool ValidateCopyTextureCHROMIUMInternalFormats(const FeatureInfo* feature_info,
+                                                GLenum source_internal_format,
+                                                GLenum dest_internal_format,
+                                                std::string* output_error_msg);
 }  // namespace gles2
 }  // namespace gpu
 
diff --git a/gpu/command_buffer/service/gles2_cmd_decoder.cc b/gpu/command_buffer/service/gles2_cmd_decoder.cc
index 1621f31..40d57831 100644
--- a/gpu/command_buffer/service/gles2_cmd_decoder.cc
+++ b/gpu/command_buffer/service/gles2_cmd_decoder.cc
@@ -2184,9 +2184,6 @@
                                            TextureRef* source_texture_ref,
                                            TextureRef* dest_texture_ref);
   bool CanUseCopyTextureCHROMIUMInternalFormat(GLenum dest_internal_format);
-  bool ValidateCopyTextureCHROMIUMInternalFormats(const char* function_name,
-                                                  GLenum source_internal_format,
-                                                  GLenum dest_internal_format);
   bool ValidateCompressedCopyTextureCHROMIUM(const char* function_name,
                                              TextureRef* source_texture_ref,
                                              TextureRef* dest_texture_ref);
@@ -17011,105 +17008,6 @@
   }
 }
 
-bool GLES2DecoderImpl::ValidateCopyTextureCHROMIUMInternalFormats(
-    const char* function_name,
-    GLenum source_internal_format,
-    GLenum dest_internal_format) {
-  bool valid_dest_format = false;
-  // TODO(qiankun.miao@intel.com): ALPHA, LUMINANCE and LUMINANCE_ALPHA formats
-  // are not supported on GL core profile. See crbug.com/577144. Enable the
-  // workaround for glCopyTexImage and glCopyTexSubImage in
-  // gles2_cmd_copy_tex_image.cc for glCopyTextureCHROMIUM implementation.
-  switch (dest_internal_format) {
-    case GL_RGB:
-    case GL_RGBA:
-    case GL_RGB8:
-    case GL_RGBA8:
-      valid_dest_format = true;
-      break;
-    case GL_BGRA_EXT:
-    case GL_BGRA8_EXT:
-      valid_dest_format =
-          feature_info_->feature_flags().ext_texture_format_bgra8888;
-      break;
-    case GL_SRGB_EXT:
-    case GL_SRGB_ALPHA_EXT:
-      valid_dest_format = feature_info_->feature_flags().ext_srgb;
-      break;
-    case GL_R8:
-    case GL_R8UI:
-    case GL_RG8:
-    case GL_RG8UI:
-    case GL_SRGB8:
-    case GL_RGB565:
-    case GL_RGB8UI:
-    case GL_SRGB8_ALPHA8:
-    case GL_RGB5_A1:
-    case GL_RGBA4:
-    case GL_RGBA8UI:
-    case GL_RGB10_A2:
-      valid_dest_format = feature_info_->IsWebGL2OrES3Context();
-      break;
-    case GL_RGB9_E5:
-    case GL_R16F:
-    case GL_R32F:
-    case GL_RG16F:
-    case GL_RG32F:
-    case GL_RGB16F:
-    case GL_RGBA16F:
-    case GL_R11F_G11F_B10F:
-      valid_dest_format = feature_info_->ext_color_buffer_float_available();
-      break;
-    case GL_RGB32F:
-      valid_dest_format =
-          feature_info_->ext_color_buffer_float_available() ||
-          feature_info_->feature_flags().chromium_color_buffer_float_rgb;
-      break;
-    case GL_RGBA32F:
-      valid_dest_format =
-          feature_info_->ext_color_buffer_float_available() ||
-          feature_info_->feature_flags().chromium_color_buffer_float_rgba;
-      break;
-    case GL_ALPHA:
-    case GL_LUMINANCE:
-    case GL_LUMINANCE_ALPHA:
-      valid_dest_format = true;
-      break;
-    default:
-      valid_dest_format = false;
-      break;
-  }
-
-  // TODO(aleksandar.stojiljkovic): Use sized internal formats: crbug.com/628064
-  bool valid_source_format =
-      source_internal_format == GL_RED || source_internal_format == GL_ALPHA ||
-      source_internal_format == GL_RGB || source_internal_format == GL_RGBA ||
-      source_internal_format == GL_RGB8 || source_internal_format == GL_RGBA8 ||
-      source_internal_format == GL_LUMINANCE ||
-      source_internal_format == GL_LUMINANCE_ALPHA ||
-      source_internal_format == GL_BGRA_EXT ||
-      source_internal_format == GL_BGRA8_EXT ||
-      source_internal_format == GL_RGB_YCBCR_420V_CHROMIUM ||
-      source_internal_format == GL_RGB_YCBCR_422_CHROMIUM ||
-      source_internal_format == GL_R16_EXT;
-  if (!valid_source_format) {
-    std::string msg = "invalid source internal format " +
-        GLES2Util::GetStringEnum(source_internal_format);
-    LOCAL_SET_GL_ERROR(GL_INVALID_OPERATION, function_name,
-                       msg.c_str());
-    return false;
-  }
-  if (!valid_dest_format) {
-    std::string msg = "invalid dest internal format " +
-        GLES2Util::GetStringEnum(dest_internal_format);
-    LOCAL_SET_GL_ERROR(GL_INVALID_OPERATION, function_name,
-                       msg.c_str());
-    return false;
-  }
-
-  return true;
-}
-
 bool GLES2DecoderImpl::ValidateCompressedCopyTextureCHROMIUM(
     const char* function_name,
     TextureRef* source_texture_ref,
@@ -17203,8 +17101,12 @@
     return;
   }
 
+  std::string output_error_msg;
   if (!ValidateCopyTextureCHROMIUMInternalFormats(
-          kFunctionName, source_internal_format, internal_format)) {
+          GetFeatureInfo(), source_internal_format, internal_format,
+          &output_error_msg)) {
+    LOCAL_SET_GL_ERROR(GL_INVALID_OPERATION, kFunctionName,
+                       output_error_msg.c_str());
     return;
   }
 
@@ -17459,8 +17361,12 @@
     return;
   }
 
+  std::string output_error_msg;
   if (!ValidateCopyTextureCHROMIUMInternalFormats(
-          function_name, source_internal_format, dest_internal_format)) {
+          GetFeatureInfo(), source_internal_format, dest_internal_format,
+          &output_error_msg)) {
+    LOCAL_SET_GL_ERROR(GL_INVALID_OPERATION, function_name,
+                       output_error_msg.c_str());
     return;
   }
 
diff --git a/gpu/command_buffer/service/raster_decoder.cc b/gpu/command_buffer/service/raster_decoder.cc
index a97a98aa..aa913772 100644
--- a/gpu/command_buffer/service/raster_decoder.cc
+++ b/gpu/command_buffer/service/raster_decoder.cc
@@ -32,7 +32,6 @@
 #include "gpu/command_buffer/common/mailbox.h"
 #include "gpu/command_buffer/common/raster_cmd_format.h"
 #include "gpu/command_buffer/common/raster_cmd_ids.h"
-#include "gpu/command_buffer/common/skia_utils.h"
 #include "gpu/command_buffer/common/sync_token.h"
 #include "gpu/command_buffer/service/buffer_manager.h"
 #include "gpu/command_buffer/service/command_buffer_service.h"
@@ -54,6 +53,7 @@
 #include "gpu/command_buffer/service/mailbox_manager.h"
 #include "gpu/command_buffer/service/query_manager.h"
 #include "gpu/command_buffer/service/raster_cmd_validation.h"
+#include "gpu/command_buffer/service/raster_decoder_context_state.h"
 #include "gpu/command_buffer/service/service_font_manager.h"
 #include "gpu/command_buffer/service/service_transfer_cache.h"
 #include "gpu/command_buffer/service/vertex_array_manager.h"
@@ -72,7 +72,6 @@
 #include "ui/gl/gl_gl_api_implementation.h"
 #include "ui/gl/gl_surface.h"
 #include "ui/gl/gl_version_info.h"
-#include "ui/gl/init/create_gr_gl_interface.h"
 
 // Local versions of the SET_GL_ERROR macros
 #define LOCAL_SET_GL_ERROR(error, function_name, msg) \
@@ -326,17 +325,19 @@
 //     PessimisticallyResetGrContext).
 //
 //   Case 2b: Executing a command that is not whitelisted: We force GL state to
-//     match |state_| as necessary (see |need_context_state_reset_|) in
+//     match |state_| as necessary (see |need_context_state_reset|) in
 //     DoCommandsImpl with RestoreState(nullptr). This will call
 //     GrContext::resetContext.
 class RasterDecoderImpl final : public RasterDecoder,
                                 public gles2::ErrorStateClient,
                                 public ServiceFontManager::Client {
  public:
-  RasterDecoderImpl(DecoderClient* client,
-                    CommandBufferServiceBase* command_buffer_service,
-                    gles2::Outputter* outputter,
-                    gles2::ContextGroup* group);
+  RasterDecoderImpl(
+      DecoderClient* client,
+      CommandBufferServiceBase* command_buffer_service,
+      gles2::Outputter* outputter,
+      gles2::ContextGroup* group,
+      scoped_refptr<RasterDecoderContextState> raster_decoder_context_state);
   ~RasterDecoderImpl() override;
 
   GLES2Util* GetGLES2Util() override { return &util_; }
@@ -476,6 +477,9 @@
   }
 
   gl::GLApi* api() const { return state_.api(); }
+  GrContext* gr_context() const {
+    return raster_decoder_context_state_->gr_context.get();
+  }
 
   const FeatureInfo::FeatureFlags& features() const {
     return feature_info_->feature_flags();
@@ -557,8 +561,8 @@
     // Calling GrContext::resetContext() is very cheap, so we do it
     // pessimistically. We could dirty less state if skia state setting
     // performance becomes an issue.
-    if (gr_context_) {
-      gr_context_->resetContext();
+    if (gr_context()) {
+      gr_context()->resetContext();
     }
   }
 
@@ -752,6 +756,7 @@
 
   // The ContextGroup for this decoder uses to track resources.
   scoped_refptr<gles2::ContextGroup> group_;
+  scoped_refptr<RasterDecoderContextState> raster_decoder_context_state_;
   std::unique_ptr<Validators> validators_;
   scoped_refptr<gles2::FeatureInfo> feature_info_;
 
@@ -793,18 +798,10 @@
 
   // Raster helpers.
   ServiceFontManager font_manager_;
-  sk_sp<GrContext> gr_context_;
   sk_sp<SkSurface> sk_surface_;
   std::unique_ptr<SkCanvas> raster_canvas_;
   uint32_t raster_color_space_id_;
   std::vector<SkDiscardableHandleId> locked_handles_;
-  size_t glyph_cache_max_texture_bytes_;
-
-  // |need_context_state_reset_| is set whenever Skia may have altered the
-  // driver's GL state. It signals the need to restore driver GL state to
-  // |state_| before executing commands that do not
-  // PermitsInconsistentContextState.
-  bool need_context_state_reset_ = false;
 
   // Tracing helpers.
   int raster_chromium_id_ = 0;
@@ -830,9 +827,10 @@
     DecoderClient* client,
     CommandBufferServiceBase* command_buffer_service,
     Outputter* outputter,
-    ContextGroup* group) {
-  return new RasterDecoderImpl(client, command_buffer_service, outputter,
-                               group);
+    ContextGroup* group,
+    scoped_refptr<RasterDecoderContextState> raster_decoder_context_state) {
+  return new RasterDecoderImpl(client, command_buffer_service, outputter, group,
+                               std::move(raster_decoder_context_state));
 }
 
 RasterDecoder::RasterDecoder(CommandBufferServiceBase* command_buffer_service,
@@ -879,11 +877,13 @@
     DecoderClient* client,
     CommandBufferServiceBase* command_buffer_service,
     Outputter* outputter,
-    ContextGroup* group)
+    ContextGroup* group,
+    scoped_refptr<RasterDecoderContextState> raster_decoder_context_state)
     : RasterDecoder(command_buffer_service, outputter),
       client_(client),
       logger_(&debug_marker_manager_, client),
       group_(group),
+      raster_decoder_context_state_(std::move(raster_decoder_context_state)),
       validators_(new Validators),
       feature_info_(group_->feature_info()),
       state_(group_->feature_info(), this, &logger_),
@@ -893,7 +893,9 @@
       gpu_decoder_category_(TRACE_EVENT_API_GET_CATEGORY_GROUP_ENABLED(
           TRACE_DISABLED_BY_DEFAULT("gpu_decoder"))),
       font_manager_(this),
-      weak_ptr_factory_(this) {}
+      weak_ptr_factory_(this) {
+  DCHECK(raster_decoder_context_state_);
+}
 
 RasterDecoderImpl::~RasterDecoderImpl() {}
 
@@ -1010,62 +1012,18 @@
       return ContextResult::kFatalFailure;
     }
 
-    // Assume that we can create a GrContext. This will be set to false if
-    // there's a failure.
-    supports_oop_raster_ = true;
-    sk_sp<const GrGLInterface> interface(
-        gl::init::CreateGrGLInterface(gl_version_info()));
-    if (!interface) {
-      DLOG(ERROR) << "OOP raster support disabled: GrGLInterface creation "
-                     "failed.";
-      supports_oop_raster_ = false;
-    }
-
+    supports_oop_raster_ = !!raster_decoder_context_state_->gr_context;
     if (supports_oop_raster_) {
-      // If you make any changes to the GrContext::Options here that could
-      // affect text rendering, make sure to match the capabilities initialized
-      // in GetCapabilities and ensuring these are also used by the
-      // PaintOpBufferSerializer.
-      GrContextOptions options;
-      options.fDriverBugWorkarounds =
-          GrDriverBugWorkarounds(workarounds().ToIntSet());
-      size_t max_resource_cache_bytes = 0u;
-      DetermineGrCacheLimitsFromAvailableMemory(
-          &max_resource_cache_bytes, &glyph_cache_max_texture_bytes_);
-      options.fGlyphCacheTextureMaximumBytes = glyph_cache_max_texture_bytes_;
-      gr_context_ = GrContext::MakeGL(std::move(interface), options);
-
-      if (gr_context_) {
-        // TODO(enne): This cache is for this decoder only and each decoder has
-        // its own cache.  This is pretty unfortunate.  This really needs to be
-        // rethought before shipping.  Most likely a different command buffer
-        // context for raster-in-gpu, with a shared gl context / gr context
-        // that different decoders can use.
-        constexpr int kMaxGaneshResourceCacheCount = 16384;
-        gr_context_->setResourceCacheLimits(kMaxGaneshResourceCacheCount,
-                                            max_resource_cache_bytes);
         transfer_cache_ = std::make_unique<ServiceTransferCache>();
-      } else {
-        bool was_lost = CheckResetStatus();
-        if (was_lost) {
-          DLOG(ERROR)
-              << "ContextResult::kTransientFailure: Could not create GrContext";
-          Destroy(true);
-          return ContextResult::kTransientFailure;
-        }
-        DLOG(ERROR) << "OOP raster support disabled: GrContext creation "
-                       "failed.";
-        supports_oop_raster_ = false;
       }
     }
-  }
 
   return ContextResult::kSuccess;
 }
 
 const ContextState* RasterDecoderImpl::GetContextState() {
-  if (need_context_state_reset_) {
-    need_context_state_reset_ = false;
+  if (raster_decoder_context_state_->need_context_state_reset) {
+    raster_decoder_context_state_->need_context_state_reset = false;
     // Returning nullptr to force full state restoration by the caller.  We do
     // this because GrContext changes to GL state are untracked in our state_.
     return nullptr;
@@ -1090,13 +1048,20 @@
       copy_texture_chromium_->Destroy();
       copy_texture_chromium_.reset();
     }
+
+    // Make sure we flush any pending skia work on this context.
+    if (sk_surface_) {
+      sk_surface_->flush();
+      sk_surface_.reset();
+    }
+    if (gr_context()) {
+      gr_context()->flush();
+    }
   } else {
     if (group_ && group_->texture_manager()) {
       group_->texture_manager()->MarkContextLost();
     }
-    if (gr_context_) {
-      gr_context_->abandonContext();
-    }
+
     state_.MarkContextLost();
   }
 
@@ -1197,10 +1162,11 @@
   // junk, which is a bug (https://crbug.com/828578).
   caps.sync_query = feature_info_->feature_flags().chromium_sync_query;
 
-  if (gr_context_) {
+  if (gr_context()) {
     caps.context_supports_distance_field_text =
-        gr_context_->supportsDistanceFieldText();
-    caps.glyph_cache_max_texture_bytes = glyph_cache_max_texture_bytes_;
+        gr_context()->supportsDistanceFieldText();
+    caps.glyph_cache_max_texture_bytes =
+        raster_decoder_context_state_->glyph_cache_max_texture_bytes;
   }
   return caps;
 }
@@ -1425,6 +1391,10 @@
     vertex_array_manager_->MarkContextLost();
   }
   state_.MarkContextLost();
+  raster_decoder_context_state_->context_lost = true;
+
+  if (gr_context())
+    gr_context()->abandonContext();
 }
 
 bool RasterDecoderImpl::CheckResetStatus() {
@@ -1549,8 +1519,8 @@
         }
       }
       if (!PermitsInconsistentContextState(command)) {
-        if (need_context_state_reset_) {
-          need_context_state_reset_ = false;
+        if (raster_decoder_context_state_->need_context_state_reset) {
+          raster_decoder_context_state_->need_context_state_reset = false;
           RestoreState(nullptr);
         }
       }
@@ -1772,9 +1742,9 @@
                          bound_texture ? bound_texture->service_id() : 0);
   DCHECK(glGetError() == GL_NO_ERROR);
 
-  if (gr_context_) {
-    gr_context_->resetContext(kPixelStore_GrGLBackendState |
-                              kTextureBinding_GrGLBackendState);
+  if (gr_context()) {
+    gr_context()->resetContext(kPixelStore_GrGLBackendState |
+                               kTextureBinding_GrGLBackendState);
   }
 
   return true;
@@ -1824,8 +1794,8 @@
     api()->glBindBufferFn(GL_PIXEL_UNPACK_BUFFER, bound_buffer->service_id());
   }
 
-  if (gr_context_) {
-    gr_context_->resetContext(kTextureBinding_GrGLBackendState);
+  if (gr_context()) {
+    gr_context()->resetContext(kTextureBinding_GrGLBackendState);
   }
   return true;
 }
@@ -2208,7 +2178,7 @@
   }
 
   ScopedTextureBinder binder(&state_, texture_manager(), texture,
-                             texture_metadata->target(), gr_context_.get());
+                             texture_metadata->target(), gr_context());
 
   texture_manager()->SetParameteri("glTexParameteri", GetErrorState(), texture,
                                    pname, param);
@@ -2241,7 +2211,7 @@
 
   {
     ScopedTextureBinder binder(&state_, texture_manager(), texture_ref,
-                               texture_metadata->target(), gr_context_.get());
+                               texture_metadata->target(), gr_context());
 
     if (image->BindTexImage(texture_metadata->target()))
       image_state = Texture::BOUND;
@@ -2290,7 +2260,7 @@
 
   if (image_state == Texture::BOUND) {
     ScopedTextureBinder binder(&state_, texture_manager(), texture_ref,
-                               texture_metadata->target(), gr_context_.get());
+                               texture_metadata->target(), gr_context());
 
     image->ReleaseTexImage(texture_metadata->target());
     texture_manager()->SetLevelInfo(texture_ref, texture_metadata->target(), 0,
@@ -2404,7 +2374,7 @@
           untyped_format, &is_cleared);
 
   ScopedTextureBinder binder(&state_, texture_manager(), texture_ref,
-                             texture_metadata.target(), gr_context_.get());
+                             texture_metadata.target(), gr_context());
   if (!texture_manager()->ValidForTarget(texture_metadata.target(), 0, width,
                                          height, 1)) {
     LOCAL_SET_GL_ERROR(GL_INVALID_VALUE, "glTexStorage2DImage",
@@ -2445,7 +2415,7 @@
   }
 
   ScopedTextureBinder binder(&state_, texture_manager(), texture_ref,
-                             texture_metadata.target(), gr_context_.get());
+                             texture_metadata.target(), gr_context());
 
   unsigned int internal_format =
       viz::TextureStorageFormat(texture_metadata.format());
@@ -2498,7 +2468,7 @@
   DCHECK(!state_.bound_pixel_unpack_buffer.get());
 
   ScopedTextureBinder binder(&state_, texture_manager(), texture_ref,
-                             texture_metadata.target(), gr_context_.get());
+                             texture_metadata.target(), gr_context());
 
   TextureManager::DoTexImageArguments args = {
       texture_metadata.target(),
@@ -2675,7 +2645,7 @@
   GLint dest_level = 0;
 
   ScopedTextureBinder binder(&state_, texture_manager(), dest_texture_ref,
-                             dest_target, gr_context_.get());
+                             dest_target, gr_context());
 
   int source_width = 0;
   int source_height = 0;
@@ -2749,6 +2719,14 @@
                        "destination texture bad dimensions.");
     return;
   }
+  std::string output_error_msg;
+  if (!ValidateCopyTextureCHROMIUMInternalFormats(
+          GetFeatureInfo(), source_internal_format, dest_internal_format,
+          &output_error_msg)) {
+    LOCAL_SET_GL_ERROR(GL_INVALID_OPERATION, "glCopySubTexture",
+                       output_error_msg.c_str());
+    return;
+  }
 
   if (feature_info_->feature_flags().desktop_srgb_support) {
     bool enable_framebuffer_srgb =
@@ -2884,8 +2862,8 @@
         DCHECK(rv) << "CopyTexImage() failed";
       }
       if (!texture_unit) {
-        RestoreCurrentTextureBindings(
-            &state_, textarget, state_.active_texture_unit, gr_context_.get());
+        RestoreCurrentTextureBindings(&state_, textarget,
+                                      state_.active_texture_unit, gr_context());
         return false;
       }
       return true;
@@ -2933,7 +2911,7 @@
     GLboolean can_use_lcd_text,
     GLint color_type,
     GLuint color_space_transfer_cache_id) {
-  if (!gr_context_) {
+  if (!gr_context()) {
     LOCAL_SET_GL_ERROR(GL_INVALID_OPERATION, "glBeginRasterCHROMIUM",
                        "chromium_raster_transport not enabled via attribs");
     return;
@@ -2946,7 +2924,7 @@
 
   DCHECK(locked_handles_.empty());
   DCHECK(!raster_canvas_);
-  need_context_state_reset_ = true;
+  raster_decoder_context_state_->need_context_state_reset = true;
 
   // This function should look identical to
   // ResourceProvider::ScopedSkSurfaceProvider.
@@ -3038,10 +3016,10 @@
   // If we can't match requested MSAA samples, don't use MSAA.
   int final_msaa_count = std::max(static_cast<int>(msaa_sample_count), 0);
   if (final_msaa_count >
-      gr_context_->maxSurfaceSampleCountForColorType(sk_color_type))
+      gr_context()->maxSurfaceSampleCountForColorType(sk_color_type))
     final_msaa_count = 0;
   sk_surface_ = SkSurface::MakeFromBackendTextureAsRenderTarget(
-      gr_context_.get(), gr_texture, kTopLeft_GrSurfaceOrigin, final_msaa_count,
+      gr_context(), gr_texture, kTopLeft_GrSurfaceOrigin, final_msaa_count,
       sk_color_type, nullptr, &surface_props);
 
   if (!sk_surface_) {
@@ -3097,7 +3075,7 @@
     return;
   }
   DCHECK(transfer_cache_);
-  need_context_state_reset_ = true;
+  raster_decoder_context_state_->need_context_state_reset = true;
 
   if (font_shm_size > 0) {
     // Deserialize fonts before raster.
@@ -3167,7 +3145,7 @@
     return;
   }
 
-  need_context_state_reset_ = true;
+  raster_decoder_context_state_->need_context_state_reset = true;
 
   raster_canvas_.reset();
   sk_surface_->prepareForExternalIO();
@@ -3204,7 +3182,7 @@
         "Attempt to use OOP transfer cache on a context without OOP raster.");
     return;
   }
-  DCHECK(gr_context_);
+  DCHECK(gr_context());
   DCHECK(transfer_cache_);
 
   // Validate the type we are about to create.
@@ -3236,7 +3214,7 @@
                                   handle_shm_id);
 
   if (!transfer_cache_->CreateLockedEntry(
-          entry_type, entry_id, handle, gr_context_.get(),
+          entry_type, entry_id, handle, gr_context(),
           base::make_span(data_memory, data_size))) {
     LOCAL_SET_GL_ERROR(GL_INVALID_VALUE, "glCreateTransferCacheEntryINTERNAL",
                        "Failure to deserialize transfer cache entry.");
diff --git a/gpu/command_buffer/service/raster_decoder.h b/gpu/command_buffer/service/raster_decoder.h
index b2600ff..150ac05d 100644
--- a/gpu/command_buffer/service/raster_decoder.h
+++ b/gpu/command_buffer/service/raster_decoder.h
@@ -24,16 +24,19 @@
 }  // namespace gles2
 
 namespace raster {
+struct RasterDecoderContextState;
 
 // This class implements the AsyncAPIInterface interface, decoding
 // RasterInterface commands and calling GL.
 class GPU_GLES2_EXPORT RasterDecoder : public DecoderContext,
                                        public CommonDecoder {
  public:
-  static RasterDecoder* Create(DecoderClient* client,
-                               CommandBufferServiceBase* command_buffer_service,
-                               gles2::Outputter* outputter,
-                               gles2::ContextGroup* group);
+  static RasterDecoder* Create(
+      DecoderClient* client,
+      CommandBufferServiceBase* command_buffer_service,
+      gles2::Outputter* outputter,
+      gles2::ContextGroup* group,
+      scoped_refptr<RasterDecoderContextState> raster_decoder_context_state);
 
   ~RasterDecoder() override;
 
diff --git a/gpu/command_buffer/service/raster_decoder_context_state.cc b/gpu/command_buffer/service/raster_decoder_context_state.cc
new file mode 100644
index 0000000..2f2d1b0
--- /dev/null
+++ b/gpu/command_buffer/service/raster_decoder_context_state.cc
@@ -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.
+
+#include "gpu/command_buffer/service/raster_decoder_context_state.h"
+
+#include "gpu/config/gpu_driver_bug_workarounds.h"
+#include "ui/gl/gl_context.h"
+#include "ui/gl/gl_share_group.h"
+#include "ui/gl/gl_surface.h"
+#include "ui/gl/init/create_gr_gl_interface.h"
+
+namespace gpu {
+namespace raster {
+
+RasterDecoderContextState::RasterDecoderContextState(
+    scoped_refptr<gl::GLShareGroup> share_group,
+    scoped_refptr<gl::GLSurface> surface,
+    scoped_refptr<gl::GLContext> context,
+    bool use_virtualized_gl_contexts)
+    : share_group(std::move(share_group)),
+      surface(std::move(surface)),
+      context(std::move(context)),
+      use_virtualized_gl_contexts(use_virtualized_gl_contexts) {}
+
+RasterDecoderContextState::~RasterDecoderContextState() {
+  if (gr_context)
+    gr_context->abandonContext();
+}
+
+void RasterDecoderContextState::InitializeGrContext(
+    const GpuDriverBugWorkarounds& workarounds) {
+  DCHECK(context->IsCurrent(surface.get()));
+
+  sk_sp<const GrGLInterface> interface(
+      gl::init::CreateGrGLInterface(*context->GetVersionInfo()));
+  if (!interface) {
+    LOG(ERROR) << "OOP raster support disabled: GrGLInterface creation "
+                  "failed.";
+    return;
+  }
+
+  // If you make any changes to the GrContext::Options here that could
+  // affect text rendering, make sure to match the capabilities initialized
+  // in GetCapabilities and ensuring these are also used by the
+  // PaintOpBufferSerializer.
+  GrContextOptions options;
+  options.fDriverBugWorkarounds =
+      GrDriverBugWorkarounds(workarounds.ToIntSet());
+  size_t max_resource_cache_bytes = 0u;
+  raster::DetermineGrCacheLimitsFromAvailableMemory(
+      &max_resource_cache_bytes, &glyph_cache_max_texture_bytes);
+  options.fGlyphCacheTextureMaximumBytes = glyph_cache_max_texture_bytes;
+  gr_context = GrContext::MakeGL(std::move(interface), options);
+  if (!gr_context) {
+    LOG(ERROR) << "OOP raster support disabled: GrContext creation "
+                  "failed.";
+  } else {
+    constexpr int kMaxGaneshResourceCacheCount = 16384;
+    gr_context->setResourceCacheLimits(kMaxGaneshResourceCacheCount,
+                                       max_resource_cache_bytes);
+  }
+}
+
+}  // namespace raster
+}  // namespace gpu
diff --git a/gpu/command_buffer/service/raster_decoder_context_state.h b/gpu/command_buffer/service/raster_decoder_context_state.h
new file mode 100644
index 0000000..273a13c2
--- /dev/null
+++ b/gpu/command_buffer/service/raster_decoder_context_state.h
@@ -0,0 +1,55 @@
+// 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 GPU_COMMAND_BUFFER_SERVICE_RASTER_DECODER_CONTEXT_STATE_H_
+#define GPU_COMMAND_BUFFER_SERVICE_RASTER_DECODER_CONTEXT_STATE_H_
+
+#include "base/memory/ref_counted.h"
+#include "gpu/command_buffer/common/skia_utils.h"
+#include "gpu/gpu_gles2_export.h"
+#include "third_party/skia/include/gpu/GrContext.h"
+
+namespace gl {
+class GLContext;
+class GLShareGroup;
+class GLSurface;
+}  // namespace gl
+
+namespace gpu {
+class GpuDriverBugWorkarounds;
+
+namespace raster {
+
+struct GPU_GLES2_EXPORT RasterDecoderContextState
+    : public base::RefCounted<RasterDecoderContextState> {
+ public:
+  RasterDecoderContextState(scoped_refptr<gl::GLShareGroup> share_group,
+                            scoped_refptr<gl::GLSurface> surface,
+                            scoped_refptr<gl::GLContext> context,
+                            bool use_virtualized_gl_contexts);
+  void InitializeGrContext(const GpuDriverBugWorkarounds& workarounds);
+
+  scoped_refptr<gl::GLShareGroup> share_group;
+  scoped_refptr<gl::GLSurface> surface;
+  scoped_refptr<gl::GLContext> context;
+  sk_sp<GrContext> gr_context;
+  bool use_virtualized_gl_contexts = false;
+  bool context_lost = false;
+  size_t glyph_cache_max_texture_bytes = 0u;
+
+  // |need_context_state_reset| is set whenever Skia may have altered the
+  // driver's GL state. It signals the need to restore driver GL state to
+  // |state_| before executing commands that do not
+  // PermitsInconsistentContextState.
+  bool need_context_state_reset = false;
+
+ private:
+  friend class base::RefCounted<RasterDecoderContextState>;
+  ~RasterDecoderContextState();
+};
+
+}  // namespace raster
+}  // namespace gpu
+
+#endif  // GPU_COMMAND_BUFFER_SERVICE_RASTER_DECODER_CONTEXT_STATE_H_
diff --git a/gpu/command_buffer/service/raster_decoder_unittest.cc b/gpu/command_buffer/service/raster_decoder_unittest.cc
index 486cca5..6837841 100644
--- a/gpu/command_buffer/service/raster_decoder_unittest.cc
+++ b/gpu/command_buffer/service/raster_decoder_unittest.cc
@@ -4,19 +4,27 @@
 
 #include "gpu/command_buffer/service/raster_decoder.h"
 
+#include <limits>
+
 #include "base/command_line.h"
+#include "base/memory/ptr_util.h"
 #include "gpu/command_buffer/common/gles2_cmd_utils.h"
 #include "gpu/command_buffer/common/mailbox.h"
 #include "gpu/command_buffer/common/raster_cmd_format.h"
 #include "gpu/command_buffer/service/context_group.h"
+#include "gpu/command_buffer/service/gles2_cmd_decoder.h"
 #include "gpu/command_buffer/service/memory_tracking.h"
 #include "gpu/command_buffer/service/program_manager.h"
 #include "gpu/command_buffer/service/query_manager.h"
+#include "gpu/command_buffer/service/raster_decoder_context_state.h"
 #include "gpu/command_buffer/service/raster_decoder_unittest_base.h"
 #include "gpu/command_buffer/service/test_helper.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "ui/gl/gl_image_stub.h"
 #include "ui/gl/gl_mock.h"
+#include "ui/gl/gl_surface_stub.h"
+#include "ui/gl/init/gl_factory.h"
+#include "ui/gl/test/gl_surface_test_support.h"
 
 using ::testing::_;
 using ::testing::Return;
@@ -449,6 +457,34 @@
   EXPECT_TRUE(texture->SafeToRenderFrom());
 }
 
+TEST_P(RasterDecoderManualInitTest, CopyTexSubImage2DValidateColorFormat) {
+  InitState init;
+  init.gl_version = "3.0";
+  init.extensions.push_back("GL_EXT_texture_storage");
+  init.extensions.push_back("GL_EXT_texture_rg");
+  InitDecoder(init);
+
+  // Create dest texture.
+  GLuint dest_texture_id = kNewClientId;
+  EXPECT_CALL(*gl_, GenTextures(1, _))
+      .WillOnce(SetArgPointee<1>(kNewServiceId))
+      .RetiresOnSaturation();
+  cmds::CreateTexture cmd;
+  cmd.Init(false /* use_buffer */, gfx::BufferUsage::GPU_READ,
+           viz::ResourceFormat::RED_8, dest_texture_id);
+  EXPECT_EQ(error::kNoError, ExecuteCmd(cmd));
+
+  // Set dimensions on source and dest textures.
+  DoTexStorage2D(client_texture_id_, 2, 2);
+  DoTexStorage2D(dest_texture_id, 2, 2);
+
+  SetScopedTextureBinderExpectations(GL_TEXTURE_2D);
+  CopySubTexture copy_cmd;
+  copy_cmd.Init(client_texture_id_, dest_texture_id, 0, 0, 0, 0, 2, 1);
+  EXPECT_EQ(error::kNoError, ExecuteCmd(copy_cmd));
+  EXPECT_EQ(GL_INVALID_OPERATION, GetGLError());
+}
+
 TEST_P(RasterDecoderTest, GLImageAttachedAfterClearLevel) {
   scoped_refptr<gl::GLImage> image(new gl::GLImageStub);
   GetImageManagerForTest()->AddImage(image.get(), kImageId);
@@ -493,5 +529,123 @@
   EXPECT_EQ(error::kDeferLaterCommands, ExecuteCmd(end_raster_cmd));
 }
 
+class RasterDecoderOOPTest : public testing::Test, DecoderClient {
+ public:
+  RasterDecoderOOPTest() : shader_translator_cache_(gpu_preferences_) {}
+
+  void SetUp() override {
+    gl::GLSurfaceTestSupport::InitializeOneOff();
+    gpu::GpuDriverBugWorkarounds workarounds;
+
+    scoped_refptr<gl::GLShareGroup> share_group = new gl::GLShareGroup();
+    scoped_refptr<gl::GLSurface> surface =
+        gl::init::CreateOffscreenGLSurface(gfx::Size());
+    scoped_refptr<gl::GLContext> context = gl::init::CreateGLContext(
+        share_group.get(), surface.get(), gl::GLContextAttribs());
+    ASSERT_TRUE(context->MakeCurrent(surface.get()));
+
+    context_state_ = new raster::RasterDecoderContextState(
+        std::move(share_group), std::move(surface), std::move(context),
+        false /* use_virtualized_gl_contexts */);
+    context_state_->InitializeGrContext(workarounds);
+
+    GpuFeatureInfo gpu_feature_info;
+    gpu_feature_info.status_values[GPU_FEATURE_TYPE_OOP_RASTERIZATION] =
+        kGpuFeatureStatusEnabled;
+    scoped_refptr<gles2::FeatureInfo> feature_info =
+        new gles2::FeatureInfo(workarounds, gpu_feature_info);
+    group_ = new gles2::ContextGroup(
+        gpu_preferences_, false, &mailbox_manager_,
+        nullptr /* memory_tracker */, &shader_translator_cache_,
+        &framebuffer_completeness_cache_, feature_info,
+        false /* bind_generates_resource */, &image_manager_,
+        nullptr /* image_factory */, nullptr /* progress_reporter */,
+        gpu_feature_info, &discardable_manager_);
+  }
+  void TearDown() override {
+    context_state_ = nullptr;
+    gl::init::ShutdownGL(false);
+  }
+
+  // DecoderClient implementation.
+  void OnConsoleMessage(int32_t id, const std::string& message) override {}
+  void CacheShader(const std::string& key, const std::string& shader) override {
+  }
+  void OnFenceSyncRelease(uint64_t release) override {}
+  bool OnWaitSyncToken(const gpu::SyncToken&) override { return false; }
+  void OnDescheduleUntilFinished() override {}
+  void OnRescheduleAfterFinished() override {}
+  void OnSwapBuffers(uint64_t swap_id, uint32_t flags) override {}
+
+  std::unique_ptr<RasterDecoder> CreateDecoder() {
+    auto decoder = base::WrapUnique(
+        RasterDecoder::Create(this, &command_buffer_service_, &outputter_,
+                              group_.get(), context_state_));
+    ContextCreationAttribs attribs;
+    attribs.enable_oop_rasterization = true;
+    attribs.enable_raster_interface = true;
+    CHECK_EQ(
+        decoder->Initialize(context_state_->surface, context_state_->context,
+                            true, gles2::DisallowedFeatures(), attribs),
+        ContextResult::kSuccess);
+    return decoder;
+  }
+
+  template <typename T>
+  error::Error ExecuteCmd(RasterDecoder* decoder, const T& cmd) {
+    static_assert(T::kArgFlags == cmd::kFixed,
+                  "T::kArgFlags should equal cmd::kFixed");
+    int entries_processed = 0;
+    return decoder->DoCommands(1, (const void*)&cmd,
+                               ComputeNumEntries(sizeof(cmd)),
+                               &entries_processed);
+  }
+
+ protected:
+  gles2::TraceOutputter outputter_;
+  FakeCommandBufferServiceBase command_buffer_service_;
+  scoped_refptr<RasterDecoderContextState> context_state_;
+
+  GpuPreferences gpu_preferences_;
+  gles2::MailboxManagerImpl mailbox_manager_;
+  gles2::ShaderTranslatorCache shader_translator_cache_;
+  gles2::FramebufferCompletenessCache framebuffer_completeness_cache_;
+  gles2::ImageManager image_manager_;
+  ServiceDiscardableManager discardable_manager_;
+  scoped_refptr<gles2::ContextGroup> group_;
+};
+
+TEST_F(RasterDecoderOOPTest, StateRestoreAcrossDecoders) {
+  // First decoder receives a skia command requiring context state reset.
+  auto decoder1 = CreateDecoder();
+  EXPECT_FALSE(context_state_->need_context_state_reset);
+  decoder1->SetUpForRasterCHROMIUMForTest();
+  cmds::EndRasterCHROMIUM end_raster_cmd;
+  end_raster_cmd.Init();
+  EXPECT_FALSE(error::IsError(ExecuteCmd(decoder1.get(), end_raster_cmd)));
+  EXPECT_TRUE(context_state_->need_context_state_reset);
+
+  // Another decoder receives a command which does not require consistent state,
+  // it should be processed without state restoration.
+  auto decoder2 = CreateDecoder();
+  decoder2->SetUpForRasterCHROMIUMForTest();
+  EXPECT_FALSE(error::IsError(ExecuteCmd(decoder2.get(), end_raster_cmd)));
+  EXPECT_TRUE(context_state_->need_context_state_reset);
+
+  // Now process a command which requires consistent state.
+  cmds::CreateTexture create_tex_cmd;
+  create_tex_cmd.Init(false, gfx::BufferUsage::GPU_READ_CPU_READ_WRITE,
+                      viz::ResourceFormat::RGBA_8888, 4);
+  EXPECT_FALSE(error::IsError(ExecuteCmd(decoder2.get(), create_tex_cmd)));
+  EXPECT_FALSE(context_state_->need_context_state_reset);
+
+  decoder1->Destroy(true);
+  context_state_->context->MakeCurrent(context_state_->surface.get());
+  decoder2->Destroy(true);
+
+  // Make sure the context is preserved across decoders.
+  EXPECT_FALSE(context_state_->gr_context->abandoned());
+}
+
 }  // namespace raster
 }  // namespace gpu
diff --git a/gpu/command_buffer/service/raster_decoder_unittest_base.cc b/gpu/command_buffer/service/raster_decoder_unittest_base.cc
index ece501a..da69047 100644
--- a/gpu/command_buffer/service/raster_decoder_unittest_base.cc
+++ b/gpu/command_buffer/service/raster_decoder_unittest_base.cc
@@ -23,6 +23,7 @@
 #include "gpu/command_buffer/service/logger.h"
 #include "gpu/command_buffer/service/mailbox_manager.h"
 #include "gpu/command_buffer/service/program_manager.h"
+#include "gpu/command_buffer/service/raster_decoder_context_state.h"
 #include "gpu/command_buffer/service/service_utils.h"
 #include "gpu/command_buffer/service/test_helper.h"
 #include "gpu/command_buffer/service/vertex_attrib_manager.h"
@@ -173,7 +174,6 @@
   for (const std::string& extension : init.extensions) {
     all_extensions += extension + " ";
   }
-  const std::string gl_version("2.1");
   const bool bind_generates_resource(false);
   const ContextType context_type(CONTEXT_TYPE_OPENGLES2);
 
@@ -204,13 +204,13 @@
   // extension support.
   context_ = new StrictMock<GLContextMock>();
   context_->SetExtensionsString(all_extensions.c_str());
-  context_->SetGLVersionString(gl_version.c_str());
+  context_->SetGLVersionString(init.gl_version.c_str());
 
   context_->GLContextStub::MakeCurrent(surface_.get());
 
   TestHelper::SetupContextGroupInitExpectations(
       gl_.get(), DisallowedFeatures(), all_extensions.c_str(),
-      gl_version.c_str(), context_type, bind_generates_resource);
+      init.gl_version.c_str(), context_type, bind_generates_resource);
 
   // We initialize the ContextGroup with a MockRasterDecoder so that
   // we can use the ContextGroup to figure out how the real RasterDecoder
@@ -265,8 +265,13 @@
   SetupInitCapabilitiesExpectations(group_->feature_info()->IsES3Capable());
   SetupInitStateExpectations(group_->feature_info()->IsES3Capable());
 
+  scoped_refptr<raster::RasterDecoderContextState> context_state =
+      new raster::RasterDecoderContextState(
+          new gl::GLShareGroup(), surface_, context_,
+          feature_info->workarounds().use_virtualized_gl_contexts);
   decoder_.reset(RasterDecoder::Create(this, command_buffer_service_.get(),
-                                       &outputter_, group_.get()));
+                                       &outputter_, group_.get(),
+                                       std::move(context_state)));
   decoder_->SetIgnoreCachedStateForTest(ignore_cached_state_for_test_);
   decoder_->GetLogger()->set_log_synthesized_gl_errors(false);
 
diff --git a/gpu/command_buffer/service/raster_decoder_unittest_base.h b/gpu/command_buffer/service/raster_decoder_unittest_base.h
index 2e19771..91e2b61 100644
--- a/gpu/command_buffer/service/raster_decoder_unittest_base.h
+++ b/gpu/command_buffer/service/raster_decoder_unittest_base.h
@@ -148,6 +148,7 @@
     std::vector<std::string> extensions = {"GL_ARB_sync"};
     bool lose_context_when_out_of_memory = false;
     gpu::GpuDriverBugWorkarounds workarounds;
+    std::string gl_version = "2.1";
   };
 
   void InitDecoder(const InitState& init);
diff --git a/gpu/command_buffer/service/service_utils.cc b/gpu/command_buffer/service/service_utils.cc
index 431a6bd2..1a241d2 100644
--- a/gpu/command_buffer/service/service_utils.cc
+++ b/gpu/command_buffer/service/service_utils.cc
@@ -37,10 +37,16 @@
 gl::GLContextAttribs GenerateGLContextAttribs(
     const ContextCreationAttribs& attribs_helper,
     const ContextGroup* context_group) {
-  DCHECK(context_group != nullptr);
+  return GenerateGLContextAttribs(attribs_helper,
+                                  context_group->use_passthrough_cmd_decoder());
+}
+
+gl::GLContextAttribs GenerateGLContextAttribs(
+    const ContextCreationAttribs& attribs_helper,
+    bool use_passthrough_cmd_decoder) {
   gl::GLContextAttribs attribs;
   attribs.gpu_preference = attribs_helper.gpu_preference;
-  if (context_group->use_passthrough_cmd_decoder()) {
+  if (use_passthrough_cmd_decoder) {
     attribs.bind_generates_resource = attribs_helper.bind_generates_resource;
     attribs.webgl_compatibility_context =
         IsWebGLContextType(attribs_helper.context_type);
diff --git a/gpu/command_buffer/service/service_utils.h b/gpu/command_buffer/service/service_utils.h
index 06e5d03..1d5d523 100644
--- a/gpu/command_buffer/service/service_utils.h
+++ b/gpu/command_buffer/service/service_utils.h
@@ -20,6 +20,10 @@
     const ContextCreationAttribs& attribs_helper,
     const ContextGroup* context_group);
 
+GPU_GLES2_EXPORT gl::GLContextAttribs GenerateGLContextAttribs(
+    const ContextCreationAttribs& attribs_helper,
+    bool use_passthrough_cmd_decoder);
+
 // Returns true if the passthrough command decoder has been requested
 GPU_GLES2_EXPORT bool UsePassthroughCommandDecoder(
     const base::CommandLine* command_line);
diff --git a/gpu/command_buffer/tests/fuzzer_main.cc b/gpu/command_buffer/tests/fuzzer_main.cc
index 0125db8..ab4cc15b 100644
--- a/gpu/command_buffer/tests/fuzzer_main.cc
+++ b/gpu/command_buffer/tests/fuzzer_main.cc
@@ -28,6 +28,7 @@
 #include "gpu/command_buffer/service/logger.h"
 #include "gpu/command_buffer/service/mailbox_manager_impl.h"
 #include "gpu/command_buffer/service/raster_decoder.h"
+#include "gpu/command_buffer/service/raster_decoder_context_state.h"
 #include "gpu/command_buffer/service/service_discardable_manager.h"
 #include "gpu/command_buffer/service/sync_point_manager.h"
 #include "gpu/command_buffer/service/transfer_buffer_manager.h"
@@ -354,9 +355,14 @@
 
 #if defined(GPU_FUZZER_USE_RASTER_DECODER)
     CHECK(feature_info->feature_flags().chromium_raster_transport);
+    scoped_refptr<raster::RasterDecoderContextState> context_state =
+        new raster::RasterDecoderContextState(
+            share_group_, surface_, context_,
+            config_.workarounds.use_virtualized_gl_contexts);
+    context_state->InitializeGrContext(config_.workarounds);
     decoder_.reset(raster::RasterDecoder::Create(
         command_buffer_.get(), command_buffer_->service(), &outputter_,
-        context_group.get()));
+        context_group.get(), std::move(context_state)));
 #else
     decoder_.reset(gles2::GLES2Decoder::Create(
         command_buffer_.get(), command_buffer_->service(), &outputter_,
diff --git a/gpu/ipc/in_process_command_buffer.cc b/gpu/ipc/in_process_command_buffer.cc
index bc3b627..be0d4715 100644
--- a/gpu/ipc/in_process_command_buffer.cc
+++ b/gpu/ipc/in_process_command_buffer.cc
@@ -45,6 +45,7 @@
 #include "gpu/command_buffer/service/memory_tracking.h"
 #include "gpu/command_buffer/service/query_manager.h"
 #include "gpu/command_buffer/service/raster_decoder.h"
+#include "gpu/command_buffer/service/raster_decoder_context_state.h"
 #include "gpu/command_buffer/service/service_utils.h"
 #include "gpu/command_buffer/service/sync_point_manager.h"
 #include "gpu/command_buffer/service/transfer_buffer_manager.h"
@@ -64,6 +65,8 @@
 #include "ui/gl/gl_image.h"
 #include "ui/gl/gl_image_shared_memory.h"
 #include "ui/gl/gl_share_group.h"
+#include "ui/gl/gl_version_info.h"
+#include "ui/gl/init/create_gr_gl_interface.h"
 #include "ui/gl/init/gl_factory.h"
 
 #if defined(OS_WIN)
@@ -275,11 +278,11 @@
   // |context_group_| instead.
   transfer_buffer_manager_ = std::make_unique<TransferBufferManager>(nullptr);
 
+  GpuDriverBugWorkarounds workarounds(
+      task_executor_->gpu_feature_info().enabled_gpu_driver_bug_workarounds);
   if (params.share_command_buffer) {
     context_group_ = params.share_command_buffer->context_group_;
   } else {
-    GpuDriverBugWorkarounds workarounds(
-        task_executor_->gpu_feature_info().enabled_gpu_driver_bug_workarounds);
     auto feature_info = base::MakeRefCounted<gles2::FeatureInfo>(
         workarounds, task_executor_->gpu_feature_info());
 
@@ -314,22 +317,6 @@
   command_buffer_ = std::make_unique<CommandBufferService>(
       this, transfer_buffer_manager_.get());
 
-  bool supports_oop_rasterization =
-      task_executor_->gpu_feature_info()
-          .status_values[GPU_FEATURE_TYPE_OOP_RASTERIZATION] ==
-      kGpuFeatureStatusEnabled;
-  if (supports_oop_rasterization && params.attribs.enable_oop_rasterization &&
-      params.attribs.enable_raster_interface &&
-      !params.attribs.enable_gles2_interface) {
-    decoder_.reset(raster::RasterDecoder::Create(this, command_buffer_.get(),
-                                                 task_executor_->outputter(),
-                                                 context_group_.get()));
-  } else {
-    decoder_.reset(gles2::GLES2Decoder::Create(this, command_buffer_.get(),
-                                               task_executor_->outputter(),
-                                               context_group_.get()));
-  }
-
   if (!surface_) {
     if (params.is_offscreen) {
       // TODO(crbug.com/832243): GLES2CommandBufferStub has additional logic for
@@ -396,31 +383,64 @@
   crash_keys::gpu_gl_context_is_virtual.Set(use_virtualized_gl_context_ ? "1"
                                                                         : "0");
 
-  if (use_virtualized_gl_context_) {
-    DCHECK(gl_share_group_);
-    scoped_refptr<gl::GLContext> real_context =
-        gl_share_group_->GetSharedContext(surface_.get());
-    if (!real_context.get()) {
-      real_context = gl::init::CreateGLContext(
-          gl_share_group_.get(), surface_.get(),
-          GenerateGLContextAttribs(params.attribs, context_group_.get()));
-      if (!real_context) {
-        // TODO(piman): This might not be fatal, we could recurse into
-        // CreateGLContext to get more info, tho it should be exceedingly
-        // rare and may not be recoverable anyway.
-        DestroyOnGpuThread();
-        LOG(ERROR) << "ContextResult::kFatalFailure: "
-                      "Failed to create shared context for virtualization.";
-        return gpu::ContextResult::kFatalFailure;
-      }
-      // Ensure that context creation did not lose track of the intended share
-      // group.
-      DCHECK(real_context->share_group() == gl_share_group_.get());
-      gl_share_group_->SetSharedContext(surface_.get(), real_context.get());
-
-      task_executor_->gpu_feature_info().ApplyToGLContext(real_context.get());
+  // TODO(khushalsagar): A lot of this initialization code is duplicated in
+  // GpuChannelManager. Pull it into a common util method.
+  scoped_refptr<gl::GLContext> real_context =
+      use_virtualized_gl_context_
+          ? gl_share_group_->GetSharedContext(surface_.get())
+          : nullptr;
+  if (!real_context) {
+    real_context = gl::init::CreateGLContext(
+        gl_share_group_.get(), surface_.get(),
+        GenerateGLContextAttribs(params.attribs, context_group_.get()));
+    if (!real_context) {
+      // TODO(piman): This might not be fatal, we could recurse into
+      // CreateGLContext to get more info, tho it should be exceedingly
+      // rare and may not be recoverable anyway.
+      DestroyOnGpuThread();
+      LOG(ERROR) << "ContextResult::kFatalFailure: "
+                    "Failed to create shared context for virtualization.";
+      return gpu::ContextResult::kFatalFailure;
     }
+    // Ensure that context creation did not lose track of the intended share
+    // group.
+    DCHECK(real_context->share_group() == gl_share_group_.get());
+    task_executor_->gpu_feature_info().ApplyToGLContext(real_context.get());
 
+    if (use_virtualized_gl_context_)
+      gl_share_group_->SetSharedContext(surface_.get(), real_context.get());
+  }
+
+  if (!real_context->MakeCurrent(surface_.get())) {
+    LOG(ERROR)
+        << "ContextResult::kTransientFailure, failed to make context current";
+    DestroyOnGpuThread();
+    return ContextResult::kTransientFailure;
+  }
+
+  bool supports_oop_rasterization =
+      task_executor_->gpu_feature_info()
+          .status_values[GPU_FEATURE_TYPE_OOP_RASTERIZATION] ==
+      kGpuFeatureStatusEnabled;
+  if (supports_oop_rasterization && params.attribs.enable_oop_rasterization &&
+      params.attribs.enable_raster_interface &&
+      !params.attribs.enable_gles2_interface) {
+    scoped_refptr<raster::RasterDecoderContextState> context_state =
+        new raster::RasterDecoderContextState(gl_share_group_, surface_,
+                                              real_context,
+                                              use_virtualized_gl_context_);
+    context_state->InitializeGrContext(workarounds);
+
+    decoder_.reset(raster::RasterDecoder::Create(
+        this, command_buffer_.get(), task_executor_->outputter(),
+        context_group_.get(), std::move(context_state)));
+  } else {
+    decoder_.reset(gles2::GLES2Decoder::Create(this, command_buffer_.get(),
+                                               task_executor_->outputter(),
+                                               context_group_.get()));
+  }
+
+  if (use_virtualized_gl_context_) {
     context_ = base::MakeRefCounted<GLContextVirtual>(
         gl_share_group_.get(), real_context.get(), decoder_->AsWeakPtr());
     if (!context_->Initialize(
@@ -434,29 +454,20 @@
                     "Failed to initialize virtual GL context.";
       return gpu::ContextResult::kFatalFailure;
     }
-  } else {
-    context_ = gl::init::CreateGLContext(
-        gl_share_group_.get(), surface_.get(),
-        GenerateGLContextAttribs(params.attribs, context_group_.get()));
-    if (!context_) {
+
+    if (!context_->MakeCurrent(surface_.get())) {
       DestroyOnGpuThread();
-      LOG(ERROR) << "ContextResult::kFatalFailure: Failed to create context.";
-      return gpu::ContextResult::kFatalFailure;
+      // The caller should retry making a context, but this one won't work.
+      LOG(ERROR) << "ContextResult::kTransientFailure: "
+                    "Could not make context current.";
+      return gpu::ContextResult::kTransientFailure;
     }
-    task_executor_->gpu_feature_info().ApplyToGLContext(context_.get());
-  }
 
-  if (!context_->MakeCurrent(surface_.get())) {
-    DestroyOnGpuThread();
-    // The caller should retry making a context, but this one won't work.
-    LOG(ERROR) << "ContextResult::kTransientFailure: "
-                  "Could not make context current.";
-    return gpu::ContextResult::kTransientFailure;
-  }
-
-  if (!context_->GetGLStateRestorer()) {
     context_->SetGLStateRestorer(
         new GLStateRestorerImpl(decoder_->AsWeakPtr()));
+  } else {
+    context_ = real_context;
+    DCHECK(context_->IsCurrent(surface_.get()));
   }
 
   if (!context_group_->has_program_cache() &&
diff --git a/gpu/ipc/service/gpu_channel_manager.cc b/gpu/ipc/service/gpu_channel_manager.cc
index 85d3b60e..37bb9da 100644
--- a/gpu/ipc/service/gpu_channel_manager.cc
+++ b/gpu/ipc/service/gpu_channel_manager.cc
@@ -32,6 +32,7 @@
 #include "gpu/ipc/service/gpu_memory_manager.h"
 #include "ui/gl/gl_bindings.h"
 #include "ui/gl/gl_share_group.h"
+#include "ui/gl/gl_version_info.h"
 #include "ui/gl/init/gl_factory.h"
 
 namespace gpu {
@@ -278,4 +279,112 @@
   discardable_manager_.HandleMemoryPressure(memory_pressure_level);
 }
 
+scoped_refptr<raster::RasterDecoderContextState>
+GpuChannelManager::GetRasterDecoderContextState(
+    const ContextCreationAttribs& requested_attribs,
+    ContextResult* result) {
+  if (raster_decoder_context_state_ &&
+      !raster_decoder_context_state_->context_lost) {
+    return raster_decoder_context_state_;
+  }
+
+  ContextCreationAttribs attribs;
+  attribs.gpu_preference = gl::PreferIntegratedGpu;
+  attribs.context_type = CONTEXT_TYPE_OPENGLES2;
+  attribs.bind_generates_resource = false;
+
+  if (attribs.gpu_preference != requested_attribs.gpu_preference ||
+      attribs.context_type != requested_attribs.context_type ||
+      attribs.bind_generates_resource !=
+          requested_attribs.bind_generates_resource) {
+    LOG(ERROR) << "ContextResult::kFatalFailure: Incompatible creation attribs "
+                  "used with RasterDecoder";
+    *result = ContextResult::kFatalFailure;
+  }
+
+  scoped_refptr<gl::GLSurface> surface = GetDefaultOffscreenSurface();
+  if (!surface) {
+    LOG(ERROR) << "Failed to create offscreen surface";
+    *result = ContextResult::kFatalFailure;
+    return nullptr;
+  }
+
+  bool use_virtualized_gl_contexts = false;
+#if defined(OS_MACOSX)
+  // Virtualize PreferIntegratedGpu contexts by default on OS X to prevent
+  // performance regressions when enabling FCM.
+  // http://crbug.com/180463
+  if (attribs.gpu_preference == gl::PreferIntegratedGpu)
+    use_virtualized_gl_contexts = true;
+#endif
+  use_virtualized_gl_contexts |=
+      gpu_driver_bug_workarounds_.use_virtualized_gl_contexts;
+  // MailboxManagerSync synchronization correctness currently depends on having
+  // only a single context. See crbug.com/510243 for details.
+  use_virtualized_gl_contexts |= mailbox_manager_->UsesSync();
+
+  const bool use_passthrough_decoder =
+      gles2::PassthroughCommandDecoderSupported() &&
+      gpu_preferences_.use_passthrough_cmd_decoder;
+  scoped_refptr<gl::GLShareGroup> share_group;
+  if (use_passthrough_decoder) {
+    share_group = new gl::GLShareGroup();
+  } else {
+    share_group = share_group_;
+  }
+
+  scoped_refptr<gl::GLContext> context =
+      use_virtualized_gl_contexts ? share_group->GetSharedContext(surface.get())
+                                  : nullptr;
+  if (!context) {
+    context = gl::init::CreateGLContext(
+        share_group.get(), surface.get(),
+        gles2::GenerateGLContextAttribs(attribs, use_passthrough_decoder));
+    if (!context) {
+      // TODO(piman): This might not be fatal, we could recurse into
+      // CreateGLContext to get more info, tho it should be exceedingly
+      // rare and may not be recoverable anyway.
+      LOG(ERROR) << "ContextResult::kFatalFailure: "
+                    "Failed to create shared context for virtualization.";
+      *result = ContextResult::kFatalFailure;
+      return nullptr;
+    }
+    // Ensure that context creation did not lose track of the intended share
+    // group.
+    DCHECK(context->share_group() == share_group.get());
+    gpu_feature_info_.ApplyToGLContext(context.get());
+
+    if (use_virtualized_gl_contexts)
+      share_group->SetSharedContext(surface.get(), context.get());
+  }
+
+  // This should be either:
+  // (1) a non-virtual GL context, or
+  // (2) a mock/stub context.
+  DCHECK(context->GetHandle() ||
+         gl::GetGLImplementation() == gl::kGLImplementationMockGL ||
+         gl::GetGLImplementation() == gl::kGLImplementationStubGL);
+
+  if (!context->MakeCurrent(surface.get())) {
+    LOG(ERROR)
+        << "ContextResult::kTransientFailure, failed to make context current";
+    *result = ContextResult::kTransientFailure;
+    return nullptr;
+  }
+
+  raster_decoder_context_state_ = new raster::RasterDecoderContextState(
+      std::move(share_group), std::move(surface), std::move(context),
+      use_virtualized_gl_contexts);
+  const bool enable_raster_transport =
+      gpu_feature_info_.status_values[GPU_FEATURE_TYPE_OOP_RASTERIZATION] ==
+      gpu::kGpuFeatureStatusEnabled;
+  if (enable_raster_transport) {
+    raster_decoder_context_state_->InitializeGrContext(
+        gpu_driver_bug_workarounds_);
+  }
+
+  *result = ContextResult::kSuccess;
+  return raster_decoder_context_state_;
+}
+
 }  // namespace gpu
diff --git a/gpu/ipc/service/gpu_channel_manager.h b/gpu/ipc/service/gpu_channel_manager.h
index 12abb2df..ef89a155 100644
--- a/gpu/ipc/service/gpu_channel_manager.h
+++ b/gpu/ipc/service/gpu_channel_manager.h
@@ -21,6 +21,7 @@
 #include "build/build_config.h"
 #include "gpu/command_buffer/common/activity_flags.h"
 #include "gpu/command_buffer/common/constants.h"
+#include "gpu/command_buffer/service/raster_decoder_context_state.h"
 #include "gpu/command_buffer/service/service_discardable_manager.h"
 #include "gpu/command_buffer/service/shader_translator_cache.h"
 #include "gpu/config/gpu_driver_bug_workarounds.h"
@@ -133,6 +134,10 @@
 
   SyncPointManager* sync_point_manager() const { return sync_point_manager_; }
 
+  scoped_refptr<raster::RasterDecoderContextState> GetRasterDecoderContextState(
+      const ContextCreationAttribs& attribs,
+      ContextResult* result);
+
  private:
   void InternalDestroyGpuMemoryBuffer(gfx::GpuMemoryBufferId id, int client_id);
   void InternalDestroyGpuMemoryBufferOnIO(gfx::GpuMemoryBufferId id,
@@ -191,6 +196,19 @@
 
   base::MemoryPressureListener memory_pressure_listener_;
 
+  // The RasterDecoderContextState is shared across all RasterDecoders. Note
+  // that this class needs to be ref-counted to conveniently manage the lifetime
+  // of the shared context in the case of a context loss. While the
+  // RasterDecoders strictly outlive the GpuChannelManager, in the event of a
+  // context loss the clients need to re-create the GpuChannel and command
+  // buffers once notified. In this interim state we can have multiple instances
+  // of the RasterDecoderContextState, for the lost and recovered clients. In
+  // order to avoid having the GpuChannelManager keep the lost context state
+  // alive until all clients have recovered, we use a ref-counted object and
+  // allow the decoders to manage its lifetime.
+  scoped_refptr<raster::RasterDecoderContextState>
+      raster_decoder_context_state_;
+
   // Member variables should appear before the WeakPtrFactory, to ensure
   // that any WeakPtrs to Controller are invalidated before its members
   // variable's destructors are executed, rendering them invalid.
diff --git a/gpu/ipc/service/raster_command_buffer_stub.cc b/gpu/ipc/service/raster_command_buffer_stub.cc
index a20bb91..b4c8183 100644
--- a/gpu/ipc/service/raster_command_buffer_stub.cc
+++ b/gpu/ipc/service/raster_command_buffer_stub.cc
@@ -88,6 +88,12 @@
     return ContextResult::kFatalFailure;
   }
 
+  if (surface_handle_ != kNullSurfaceHandle) {
+    LOG(ERROR) << "ContextResult::kFatalFailure: "
+                  "RenderInterface clients must render offscreen.";
+    return gpu::ContextResult::kFatalFailure;
+  }
+
   scoped_refptr<gles2::FeatureInfo> feature_info = new gles2::FeatureInfo(
       manager->gpu_driver_bug_workarounds(), manager->gpu_feature_info());
   gpu::GpuMemoryBufferFactory* gmb_factory =
@@ -102,87 +108,37 @@
       manager->watchdog() /* progress_reporter */, manager->gpu_feature_info(),
       manager->discardable_manager());
 
-#if defined(OS_MACOSX)
-  // Virtualize PreferIntegratedGpu contexts by default on OS X to prevent
-  // performance regressions when enabling FCM.
-  // http://crbug.com/180463
-  if (init_params.attribs.gpu_preference == gl::PreferIntegratedGpu)
-    use_virtualized_gl_context_ = true;
-#endif
-
-  use_virtualized_gl_context_ |=
-      context_group_->feature_info()->workarounds().use_virtualized_gl_contexts;
-
-  // MailboxManagerSync synchronization correctness currently depends on having
-  // only a single context. See crbug.com/510243 for details.
-  use_virtualized_gl_context_ |= manager->mailbox_manager()->UsesSync();
-
-  if (surface_handle_ != kNullSurfaceHandle) {
+  ContextResult result;
+  auto raster_decoder_context_state =
+      manager->GetRasterDecoderContextState(init_params.attribs, &result);
+  if (!raster_decoder_context_state) {
     LOG(ERROR) << "ContextResult::kFatalFailure: "
-                  "RenderInterface clients must render offscreen.";
-    return gpu::ContextResult::kFatalFailure;
+                  "Failed to create raster decoder state.";
+    DCHECK_NE(result, gpu::ContextResult::kSuccess);
+    return result;
   }
 
-  surface_ = manager->GetDefaultOffscreenSurface();
-  if (!surface_) {
-    LOG(ERROR) << "ContextResult::kFatalFailure: "
-                  "Failed to create default offscreen surface.";
-    return gpu::ContextResult::kFatalFailure;
-  }
+  surface_ = raster_decoder_context_state->surface;
+  share_group_ = raster_decoder_context_state->share_group;
+  use_virtualized_gl_context_ =
+      raster_decoder_context_state->use_virtualized_gl_contexts;
 
   command_buffer_ = std::make_unique<CommandBufferService>(
       this, context_group_->transfer_buffer_manager());
   std::unique_ptr<raster::RasterDecoder> decoder(raster::RasterDecoder::Create(
-      this, command_buffer_.get(), manager->outputter(), context_group_.get()));
+      this, command_buffer_.get(), manager->outputter(), context_group_.get(),
+      raster_decoder_context_state));
 
   sync_point_client_state_ =
       channel_->sync_point_manager()->CreateSyncPointClientState(
           CommandBufferNamespace::GPU_IO, command_buffer_id_, sequence_id_);
 
-  if (context_group_->use_passthrough_cmd_decoder()) {
-    // When using the passthrough command decoder, only share with other
-    // contexts in the explicitly requested share group
-      share_group_ = new gl::GLShareGroup();
-  } else {
-    // When using the validating command decoder, always use the global share
-    // group
-    share_group_ = channel_->share_group();
-  }
-
   // TODO(sunnyps): Should this use ScopedCrashKey instead?
   crash_keys::gpu_gl_context_is_virtual.Set(use_virtualized_gl_context_ ? "1"
                                                                         : "0");
 
-  scoped_refptr<gl::GLContext> context;
+  scoped_refptr<gl::GLContext> context = raster_decoder_context_state->context;
   if (use_virtualized_gl_context_) {
-    context = share_group_->GetSharedContext(surface_.get());
-    if (!context) {
-      context = gl::init::CreateGLContext(
-          share_group_.get(), surface_.get(),
-          GenerateGLContextAttribs(init_params.attribs, context_group_.get()));
-      if (!context) {
-        // TODO(piman): This might not be fatal, we could recurse into
-        // CreateGLContext to get more info, tho it should be exceedingly
-        // rare and may not be recoverable anyway.
-        LOG(ERROR) << "ContextResult::kFatalFailure: "
-                      "Failed to create shared context for virtualization.";
-        return gpu::ContextResult::kFatalFailure;
-      }
-      // Ensure that context creation did not lose track of the intended share
-      // group.
-      DCHECK(context->share_group() == share_group_.get());
-      share_group_->SetSharedContext(surface_.get(), context.get());
-
-      // This needs to be called against the real shared context, not the
-      // virtual context created below.
-      manager->gpu_feature_info().ApplyToGLContext(context.get());
-    }
-    // This should be either:
-    // (1) a non-virtual GL context, or
-    // (2) a mock/stub context.
-    DCHECK(context->GetHandle() ||
-           gl::GetGLImplementation() == gl::kGLImplementationMockGL ||
-           gl::GetGLImplementation() == gl::kGLImplementationStubGL);
     context = base::MakeRefCounted<GLContextVirtual>(
         share_group_.get(), context.get(), decoder->AsWeakPtr());
     if (!context->Initialize(surface_.get(),
@@ -198,19 +154,8 @@
                     "Failed to initialize virtual GL context.";
       return gpu::ContextResult::kFatalFailure;
     }
-  } else {
-    context = gl::init::CreateGLContext(
-        share_group_.get(), surface_.get(),
-        GenerateGLContextAttribs(init_params.attribs, context_group_.get()));
-    if (!context) {
-      // TODO(piman): This might not be fatal, we could recurse into
-      // CreateGLContext to get more info, tho it should be exceedingly
-      // rare and may not be recoverable anyway.
-      LOG(ERROR) << "ContextResult::kFatalFailure: Failed to create context.";
-      return gpu::ContextResult::kFatalFailure;
-    }
 
-    manager->gpu_feature_info().ApplyToGLContext(context.get());
+    context->SetGLStateRestorer(new GLStateRestorerImpl(decoder->AsWeakPtr()));
   }
 
   if (!context->MakeCurrent(surface_.get())) {
@@ -219,19 +164,15 @@
     return gpu::ContextResult::kTransientFailure;
   }
 
-  if (!context->GetGLStateRestorer()) {
-    context->SetGLStateRestorer(new GLStateRestorerImpl(decoder->AsWeakPtr()));
-  }
-
   if (!context_group_->has_program_cache() &&
       !context_group_->feature_info()->workarounds().disable_program_cache) {
     context_group_->set_program_cache(manager->program_cache());
   }
 
   // Initialize the decoder with either the view or pbuffer GLContext.
-  auto result = decoder->Initialize(surface_, context, true /* offscreen */,
-                                    gpu::gles2::DisallowedFeatures(),
-                                    init_params.attribs);
+  result = decoder->Initialize(surface_, context, true /* offscreen */,
+                               gpu::gles2::DisallowedFeatures(),
+                               init_params.attribs);
   if (result != gpu::ContextResult::kSuccess) {
     DLOG(ERROR) << "Failed to initialize decoder.";
     return result;
diff --git a/headless/lib/browser/devtools_api/client_api_generator.py b/headless/lib/browser/devtools_api/client_api_generator.py
index 118b865..eb74c6a 100644
--- a/headless/lib/browser/devtools_api/client_api_generator.py
+++ b/headless/lib/browser/devtools_api/client_api_generator.py
@@ -292,9 +292,8 @@
           type_definitions[domain['domain'] + '.' + type['id']] = (
               CreateObjectTypeDefinition())
       elif type['type'] == 'array':
-        items_type = type['items']['type']
         type_definitions[domain['domain'] + '.' + type['id']] = (
-            WrapArrayDefinition(type_definitions[items_type]))
+            ResolveType(type))
       elif 'enum' in type:
         type_definitions[domain['domain'] + '.' + type['id']] = (
             CreateEnumTypeDefinition(domain['domain'], type))
diff --git a/ios/chrome/browser/app_launcher/app_launcher_tab_helper_unittest.mm b/ios/chrome/browser/app_launcher/app_launcher_tab_helper_unittest.mm
index 6cd04b11..ff1156d1 100644
--- a/ios/chrome/browser/app_launcher/app_launcher_tab_helper_unittest.mm
+++ b/ios/chrome/browser/app_launcher/app_launcher_tab_helper_unittest.mm
@@ -82,7 +82,8 @@
                             bool has_user_gesture) WARN_UNUSED_RESULT {
     NSURL* url = [NSURL URLWithString:url_string];
     web::WebStatePolicyDecider::RequestInfo request_info(
-        ui::PageTransition::PAGE_TRANSITION_LINK, target_frame_is_main,
+        ui::PageTransition::PAGE_TRANSITION_LINK,
+        /*source_url=*/GURL::EmptyGURL(), target_frame_is_main,
         has_user_gesture);
     return tab_helper_->ShouldAllowRequest([NSURLRequest requestWithURL:url],
                                            request_info);
diff --git a/ios/chrome/browser/itunes_urls/itunes_urls_handler_tab_helper_unittest.mm b/ios/chrome/browser/itunes_urls/itunes_urls_handler_tab_helper_unittest.mm
index 43c272a6..638c0bc 100644
--- a/ios/chrome/browser/itunes_urls/itunes_urls_handler_tab_helper_unittest.mm
+++ b/ios/chrome/browser/itunes_urls/itunes_urls_handler_tab_helper_unittest.mm
@@ -43,7 +43,8 @@
     fake_launcher_.launchedProductID = nil;
     fake_launcher_.launchedProductParams = nil;
     web::WebStatePolicyDecider::RequestInfo request_info(
-        ui::PageTransition::PAGE_TRANSITION_LINK, main_frame,
+        ui::PageTransition::PAGE_TRANSITION_LINK,
+        /*source_url=*/GURL::EmptyGURL(), main_frame,
         /*has_user_gesture=*/false);
     bool request_allowed = web_state_.ShouldAllowRequest(
         [NSURLRequest requestWithURL:[NSURL URLWithString:url_string]],
diff --git a/ios/chrome/browser/ui/history/history_clear_browsing_data_coordinator.h b/ios/chrome/browser/ui/history/history_clear_browsing_data_coordinator.h
index 8352514..070b25d 100644
--- a/ios/chrome/browser/ui/history/history_clear_browsing_data_coordinator.h
+++ b/ios/chrome/browser/ui/history/history_clear_browsing_data_coordinator.h
@@ -8,6 +8,7 @@
 #import "ios/chrome/browser/ui/coordinators/chrome_coordinator.h"
 #import "ios/chrome/browser/ui/settings/clear_browsing_data_local_commands.h"
 
+@protocol ApplicationCommands;
 @protocol UrlLoader;
 @protocol HistoryLocalCommands;
 @protocol HistoryPresentationDelegate;
@@ -21,6 +22,9 @@
 // Delegate for this coordinator.
 @property(nonatomic, weak) id<HistoryLocalCommands> localDispatcher;
 
+// Dispatcher for view controller.
+@property(nonatomic, weak) id<ApplicationCommands> dispatcher;
+
 // The UrlLoader used by this coordinator.
 @property(nonatomic, weak) id<UrlLoader> loader;
 
diff --git a/ios/chrome/browser/ui/history/history_clear_browsing_data_coordinator.mm b/ios/chrome/browser/ui/history/history_clear_browsing_data_coordinator.mm
index 87777c73..64907fd 100644
--- a/ios/chrome/browser/ui/history/history_clear_browsing_data_coordinator.mm
+++ b/ios/chrome/browser/ui/history/history_clear_browsing_data_coordinator.mm
@@ -26,6 +26,7 @@
 @end
 
 @implementation HistoryClearBrowsingDataCoordinator
+@synthesize dispatcher = _dispatcher;
 @synthesize historyClearBrowsingDataNavigationController =
     _historyClearBrowsingDataNavigationController;
 @synthesize loader = _loader;
@@ -38,6 +39,7 @@
           initWithBrowserState:self.browserState];
   clearBrowsingDataTableViewController.extendedLayoutIncludesOpaqueBars = YES;
   clearBrowsingDataTableViewController.localDispatcher = self;
+  clearBrowsingDataTableViewController.dispatcher = self.dispatcher;
   // Configure and present ClearBrowsingDataNavigationController.
   self.historyClearBrowsingDataNavigationController =
       [[TableViewNavigationController alloc]
diff --git a/ios/chrome/browser/ui/history/history_coordinator.mm b/ios/chrome/browser/ui/history/history_coordinator.mm
index 99df0e5..c5a3f9d7 100644
--- a/ios/chrome/browser/ui/history/history_coordinator.mm
+++ b/ios/chrome/browser/ui/history/history_coordinator.mm
@@ -127,6 +127,7 @@
     self.historyClearBrowsingDataCoordinator.presentationDelegate =
         self.presentationDelegate;
     self.historyClearBrowsingDataCoordinator.loader = self.loader;
+    self.historyClearBrowsingDataCoordinator.dispatcher = self.dispatcher;
     [self.historyClearBrowsingDataCoordinator start];
   } else {
     [self.dispatcher showClearBrowsingDataSettingsFromViewController:
diff --git a/ios/chrome/browser/ui/settings/clear_browsing_data_consumer.h b/ios/chrome/browser/ui/settings/clear_browsing_data_consumer.h
index 4891a80..bed38d23c 100644
--- a/ios/chrome/browser/ui/settings/clear_browsing_data_consumer.h
+++ b/ios/chrome/browser/ui/settings/clear_browsing_data_consumer.h
@@ -22,17 +22,22 @@
 enum class BrowsingDataRemoveMask;
 
 @protocol ClearBrowsingDataConsumer<NSObject>
-// Updates contents of a cell for a given item.
-- (void)updateCellsForItem:(ListItem*)item;
-// Updates item of |itemType| with |detailText|.
-- (void)updateCounter:(NSInteger)itemType detailText:(NSString*)detailText;
-// Indicate to user that data has been cleared.
-- (void)showBrowsingHistoryRemovedDialog;
 // Execute action to clear browsing data.
 - (void)removeBrowsingDataForBrowserState:(ios::ChromeBrowserState*)browserState
                                timePeriod:(browsing_data::TimePeriod)timePeriod
                                removeMask:(BrowsingDataRemoveMask)removeMask
                           completionBlock:(ProceduralBlock)completionBlock;
+// Updates contents of a cell for a given item.
+- (void)updateCellsForItem:(ListItem*)item;
+
+// Only necessary for ClearBrowsingDataCollectionView to implement
+// IsNewClearBrowsingDataUIEnabled experiment and to show a dialog about other
+// forms of browsing history.
+@optional
+// Updates item of |itemType| with |detailText|.
+- (void)updateCounter:(NSInteger)itemType detailText:(NSString*)detailText;
+// Indicate to user that data has been cleared.
+- (void)showBrowsingHistoryRemovedDialog;
 @end
 
 #endif  // IOS_CHROME_BROWSER_UI_SETTINGS_CLEAR_BROWSING_DATA_CONSUMER_H_
diff --git a/ios/chrome/browser/ui/settings/clear_browsing_data_table_view_controller.h b/ios/chrome/browser/ui/settings/clear_browsing_data_table_view_controller.h
index 06027a4..1ee8d71 100644
--- a/ios/chrome/browser/ui/settings/clear_browsing_data_table_view_controller.h
+++ b/ios/chrome/browser/ui/settings/clear_browsing_data_table_view_controller.h
@@ -11,6 +11,7 @@
 class ChromeBrowserState;
 }
 
+@protocol ApplicationCommands;
 @protocol ClearBrowsingDataLocalCommands;
 
 // TableView for clearing browsing data (including history,
@@ -28,6 +29,9 @@
 // Local Dispatcher for this ClearBrowsingDataTableView.
 @property(nonatomic, weak) id<ClearBrowsingDataLocalCommands> localDispatcher;
 
+// The dispatcher used by this ViewController.
+@property(nonatomic, weak) id<ApplicationCommands> dispatcher;
+
 @end
 
 #endif  // IOS_CHROME_BROWSER_UI_HISTORY_CLEAR_BROWSING_DATA_TABLE_VIEW_CONTROLLER_H_
diff --git a/ios/chrome/browser/ui/settings/clear_browsing_data_table_view_controller.mm b/ios/chrome/browser/ui/settings/clear_browsing_data_table_view_controller.mm
index 2abb57f..fa4ecd3 100644
--- a/ios/chrome/browser/ui/settings/clear_browsing_data_table_view_controller.mm
+++ b/ios/chrome/browser/ui/settings/clear_browsing_data_table_view_controller.mm
@@ -6,6 +6,7 @@
 
 #include "base/mac/foundation_util.h"
 #include "ios/chrome/browser/browsing_data/browsing_data_remove_mask.h"
+#import "ios/chrome/browser/ui/commands/application_commands.h"
 #import "ios/chrome/browser/ui/settings/cells/table_view_clear_browsing_data_item.h"
 #include "ios/chrome/browser/ui/settings/clear_browsing_data_local_commands.h"
 #import "ios/chrome/browser/ui/settings/clear_browsing_data_manager.h"
@@ -30,7 +31,8 @@
 }
 
 @interface ClearBrowsingDataTableViewController ()<
-    TableViewTextLinkCellDelegate>
+    TableViewTextLinkCellDelegate,
+    ClearBrowsingDataConsumer>
 
 // TODO(crbug.com/850699): remove direct dependency and replace with
 // delegate.
@@ -44,6 +46,7 @@
 @implementation ClearBrowsingDataTableViewController
 @synthesize browserState = _browserState;
 @synthesize dataManager = _dataManager;
+@synthesize dispatcher = _dispatcher;
 @synthesize localDispatcher = _localDispatcher;
 
 #pragma mark - ViewController Lifecycle.
@@ -56,6 +59,7 @@
     _dataManager = [[ClearBrowsingDataManager alloc]
         initWithBrowserState:browserState
                     listType:ClearBrowsingDataListType::kListTypeTableView];
+    _dataManager.consumer = self;
   }
   return self;
 }
@@ -180,6 +184,22 @@
   [self.localDispatcher openURL:copiedURL];
 }
 
+#pragma mark - ClearBrowsingDataConsumer
+
+- (void)updateCellsForItem:(ListItem*)item {
+  [self reconfigureCellsForItems:@[ item ]];
+}
+
+- (void)removeBrowsingDataForBrowserState:(ios::ChromeBrowserState*)browserState
+                               timePeriod:(browsing_data::TimePeriod)timePeriod
+                               removeMask:(BrowsingDataRemoveMask)removeMask
+                          completionBlock:(ProceduralBlock)completionBlock {
+  [self.dispatcher removeBrowsingDataForBrowserState:browserState
+                                          timePeriod:timePeriod
+                                          removeMask:removeMask
+                                     completionBlock:completionBlock];
+}
+
 #pragma mark - Private Helpers
 
 - (void)showClearBrowsingDataAlertController {
diff --git a/ios/web/browser_state.mm b/ios/web/browser_state.mm
index ac68d8e..58d35374 100644
--- a/ios/web/browser_state.mm
+++ b/ios/web/browser_state.mm
@@ -171,8 +171,10 @@
     DCHECK(!network_context_);
     DCHECK(!network_context_owner_);
 
+    net::URLRequestContextGetter* request_context = GetRequestContext();
+    DCHECK(request_context);
     network_context_owner_ = std::make_unique<NetworkContextOwner>(
-        GetRequestContext(), &network_context_);
+        request_context, &network_context_);
     auto url_loader_factory_params =
         network::mojom::URLLoaderFactoryParams::New();
     url_loader_factory_params->process_id = network::mojom::kBrowserProcessId;
diff --git a/ios/web/public/web_state/web_state_policy_decider.h b/ios/web/public/web_state/web_state_policy_decider.h
index fac0fe34..0ccdbac 100644
--- a/ios/web/public/web_state/web_state_policy_decider.h
+++ b/ios/web/public/web_state/web_state_policy_decider.h
@@ -9,6 +9,7 @@
 
 #include "base/macros.h"
 #include "ui/base/page_transition_types.h"
+#include "url/gurl.h"
 
 namespace web {
 
@@ -22,14 +23,18 @@
   // request passed to WebStatePolicyDecider::ShouldAllowRequest().
   struct RequestInfo {
     RequestInfo(ui::PageTransition transition_type,
+                const GURL& source_url,
                 bool target_frame_is_main,
                 bool has_user_gesture)
         : transition_type(transition_type),
+          source_url(source_url),
           target_frame_is_main(target_frame_is_main),
           has_user_gesture(has_user_gesture) {}
     // The navigation page transition type.
     ui::PageTransition transition_type =
         ui::PageTransition::PAGE_TRANSITION_FIRST;
+    // The source URL that the request was initiated from.
+    GURL source_url;
     // Indicates whether the navigation target frame is the main frame.
     bool target_frame_is_main = false;
     // Indicates if there was a recent user interaction with the request frame.
diff --git a/ios/web/web_state/navigation_and_load_callbacks_inttest.mm b/ios/web/web_state/navigation_and_load_callbacks_inttest.mm
index e003186..f238f08 100644
--- a/ios/web/web_state/navigation_and_load_callbacks_inttest.mm
+++ b/ios/web/web_state/navigation_and_load_callbacks_inttest.mm
@@ -476,6 +476,7 @@
 MATCHER_P(RequestInfoMatch, expected_request_info, /* argument_name = */ "") {
   return ui::PageTransitionTypeIncludingQualifiersIs(
              arg.transition_type, expected_request_info.transition_type) &&
+         arg.source_url == expected_request_info.source_url &&
          arg.target_frame_is_main ==
              expected_request_info.target_frame_is_main &&
          arg.has_user_gesture == expected_request_info.has_user_gesture;
@@ -587,7 +588,7 @@
   int32_t nav_id = 0;
   EXPECT_CALL(observer_, DidStartLoading(web_state()));
   WebStatePolicyDecider::RequestInfo expected_request_info(
-      ui::PageTransition::PAGE_TRANSITION_CLIENT_REDIRECT,
+      ui::PageTransition::PAGE_TRANSITION_CLIENT_REDIRECT, url,
       /*target_main_frame=*/true, /*has_user_gesture=*/false);
   EXPECT_CALL(*decider_,
               ShouldAllowRequest(_, RequestInfoMatch(expected_request_info)))
@@ -620,7 +621,7 @@
   int32_t nav_id = 0;
   EXPECT_CALL(observer_, DidStartLoading(web_state()));
   WebStatePolicyDecider::RequestInfo expected_request_info(
-      ui::PageTransition::PAGE_TRANSITION_CLIENT_REDIRECT,
+      ui::PageTransition::PAGE_TRANSITION_CLIENT_REDIRECT, url,
       /*target_main_frame=*/true, /*has_user_gesture=*/false);
   EXPECT_CALL(*decider_,
               ShouldAllowRequest(_, RequestInfoMatch(expected_request_info)))
@@ -652,7 +653,7 @@
   int32_t nav_id = 0;
   EXPECT_CALL(observer_, DidStartLoading(web_state()));
   WebStatePolicyDecider::RequestInfo expected_request_info(
-      ui::PageTransition::PAGE_TRANSITION_CLIENT_REDIRECT,
+      ui::PageTransition::PAGE_TRANSITION_CLIENT_REDIRECT, url,
       /*target_main_frame=*/true, /*has_user_gesture=*/false);
   EXPECT_CALL(*decider_,
               ShouldAllowRequest(_, RequestInfoMatch(expected_request_info)))
@@ -688,7 +689,7 @@
   // Perform new page navigation.
   EXPECT_CALL(observer_, DidStartLoading(web_state()));
   WebStatePolicyDecider::RequestInfo expected_request_info(
-      ui::PageTransition::PAGE_TRANSITION_CLIENT_REDIRECT,
+      ui::PageTransition::PAGE_TRANSITION_CLIENT_REDIRECT, url,
       /*target_main_frame=*/true, /*has_user_gesture=*/false);
   EXPECT_CALL(*decider_,
               ShouldAllowRequest(_, RequestInfoMatch(expected_request_info)))
@@ -707,7 +708,8 @@
   int32_t nav_id = 0;
   EXPECT_CALL(observer_, DidStartLoading(web_state()));
   WebStatePolicyDecider::RequestInfo reload_request_info(
-      ui::PageTransition::PAGE_TRANSITION_RELOAD, /*target_main_frame=*/true,
+      ui::PageTransition::PAGE_TRANSITION_RELOAD, url,
+      /*target_main_frame=*/true,
       /*has_user_gesture=*/false);
   EXPECT_CALL(*decider_,
               ShouldAllowRequest(_, RequestInfoMatch(reload_request_info)))
@@ -741,7 +743,7 @@
   // Perform new page navigation.
   EXPECT_CALL(observer_, DidStartLoading(web_state()));
   WebStatePolicyDecider::RequestInfo expected_request_info(
-      ui::PageTransition::PAGE_TRANSITION_CLIENT_REDIRECT,
+      ui::PageTransition::PAGE_TRANSITION_CLIENT_REDIRECT, url,
       /*target_main_frame=*/true, /*has_user_gesture=*/false);
   EXPECT_CALL(*decider_,
               ShouldAllowRequest(_, RequestInfoMatch(expected_request_info)))
@@ -788,7 +790,7 @@
   int32_t nav_id = 0;
   EXPECT_CALL(observer_, DidStartLoading(web_state()));
   WebStatePolicyDecider::RequestInfo expected_request_info(
-      ui::PageTransition::PAGE_TRANSITION_CLIENT_REDIRECT,
+      ui::PageTransition::PAGE_TRANSITION_CLIENT_REDIRECT, url,
       /*target_main_frame=*/true, /*has_user_gesture=*/false);
   EXPECT_CALL(*decider_,
               ShouldAllowRequest(_, RequestInfoMatch(expected_request_info)))
@@ -809,9 +811,14 @@
 
   // Perform same-document navigation.
   const GURL hash_url = test_server_->GetURL("/echo#1");
+  WebStatePolicyDecider::RequestInfo hash_url_expected_request_info_(
+      ui::PageTransition::PAGE_TRANSITION_CLIENT_REDIRECT, hash_url,
+      /*target_main_frame=*/true, /*has_user_gesture=*/false);
+
   EXPECT_CALL(observer_, DidStartLoading(web_state()));
-  EXPECT_CALL(*decider_,
-              ShouldAllowRequest(_, RequestInfoMatch(expected_request_info)))
+  EXPECT_CALL(
+      *decider_,
+      ShouldAllowRequest(_, RequestInfoMatch(hash_url_expected_request_info_)))
       .WillOnce(Return(true));
   EXPECT_CALL(observer_, DidStartNavigation(web_state(), _))
       .WillOnce(VerifySameDocumentStartedContext(
@@ -861,7 +868,7 @@
   int32_t nav_id = 0;
   EXPECT_CALL(observer_, DidStartLoading(web_state()));
   WebStatePolicyDecider::RequestInfo expected_request_info(
-      ui::PageTransition::PAGE_TRANSITION_CLIENT_REDIRECT,
+      ui::PageTransition::PAGE_TRANSITION_CLIENT_REDIRECT, url,
       /*target_main_frame=*/true, /*has_user_gesture=*/false);
   EXPECT_CALL(*decider_,
               ShouldAllowRequest(_, RequestInfoMatch(expected_request_info)))
@@ -915,7 +922,7 @@
   int32_t nav_id = 0;
   EXPECT_CALL(observer_, DidStartLoading(web_state()));
   WebStatePolicyDecider::RequestInfo expected_request_info(
-      ui::PageTransition::PAGE_TRANSITION_CLIENT_REDIRECT,
+      ui::PageTransition::PAGE_TRANSITION_CLIENT_REDIRECT, url,
       /*target_main_frame=*/true, /*has_user_gesture=*/false);
   EXPECT_CALL(*decider_,
               ShouldAllowRequest(_, RequestInfoMatch(expected_request_info)))
@@ -1033,7 +1040,7 @@
   int32_t nav_id = 0;
   EXPECT_CALL(observer_, DidStartLoading(web_state()));
   WebStatePolicyDecider::RequestInfo expected_request_info(
-      ui::PageTransition::PAGE_TRANSITION_CLIENT_REDIRECT,
+      ui::PageTransition::PAGE_TRANSITION_CLIENT_REDIRECT, url,
       /*target_main_frame=*/true, /*has_user_gesture=*/false);
   EXPECT_CALL(*decider_,
               ShouldAllowRequest(_, RequestInfoMatch(expected_request_info)))
@@ -1070,7 +1077,7 @@
   // Perform new page navigation.
   EXPECT_CALL(observer_, DidStartLoading(web_state()));
   WebStatePolicyDecider::RequestInfo expected_request_info(
-      ui::PageTransition::PAGE_TRANSITION_CLIENT_REDIRECT,
+      ui::PageTransition::PAGE_TRANSITION_CLIENT_REDIRECT, url,
       /*target_main_frame=*/true, /*has_user_gesture=*/false);
   EXPECT_CALL(*decider_,
               ShouldAllowRequest(_, RequestInfoMatch(expected_request_info)))
@@ -1090,7 +1097,7 @@
   NavigationContext* context = nullptr;
   int32_t nav_id = 0;
   WebStatePolicyDecider::RequestInfo form_request_info(
-      ui::PageTransition::PAGE_TRANSITION_FORM_SUBMIT,
+      ui::PageTransition::PAGE_TRANSITION_FORM_SUBMIT, url,
       /*target_main_frame=*/true, /*has_user_gesture=*/false);
   EXPECT_CALL(*decider_,
               ShouldAllowRequest(_, RequestInfoMatch(form_request_info)))
@@ -1125,7 +1132,7 @@
   // Perform new page navigation.
   EXPECT_CALL(observer_, DidStartLoading(web_state()));
   WebStatePolicyDecider::RequestInfo expected_request_info(
-      ui::PageTransition::PAGE_TRANSITION_CLIENT_REDIRECT,
+      ui::PageTransition::PAGE_TRANSITION_CLIENT_REDIRECT, url,
       /*target_main_frame=*/true, /*has_user_gesture=*/false);
   EXPECT_CALL(*decider_,
               ShouldAllowRequest(_, RequestInfoMatch(expected_request_info)))
@@ -1143,7 +1150,7 @@
 
   // Submit the form using JavaScript.
   WebStatePolicyDecider::RequestInfo form_request_info(
-      ui::PageTransition::PAGE_TRANSITION_FORM_SUBMIT,
+      ui::PageTransition::PAGE_TRANSITION_FORM_SUBMIT, url,
       /*target_main_frame=*/true, /*has_user_gesture=*/false);
   EXPECT_CALL(*decider_,
               ShouldAllowRequest(_, RequestInfoMatch(form_request_info)))
@@ -1171,8 +1178,12 @@
     // ShouldAllowRequest() not called because SlimNavigationManager catches
     // repost before calling policy decider.
   } else {
-    EXPECT_CALL(*decider_,
-                ShouldAllowRequest(_, RequestInfoMatch(form_request_info)))
+    WebStatePolicyDecider::RequestInfo form_reload_request_info(
+        ui::PageTransition::PAGE_TRANSITION_FORM_SUBMIT, action,
+        /*target_main_frame=*/true, /*has_user_gesture=*/false);
+
+    EXPECT_CALL(*decider_, ShouldAllowRequest(
+                               _, RequestInfoMatch(form_reload_request_info)))
         .WillOnce(Return(true));
   }
 
@@ -1215,7 +1226,7 @@
   // Perform new page navigation.
   EXPECT_CALL(observer_, DidStartLoading(web_state()));
   WebStatePolicyDecider::RequestInfo expected_request_info(
-      ui::PageTransition::PAGE_TRANSITION_CLIENT_REDIRECT,
+      ui::PageTransition::PAGE_TRANSITION_CLIENT_REDIRECT, url,
       /*target_main_frame=*/true, /*has_user_gesture=*/false);
   EXPECT_CALL(*decider_,
               ShouldAllowRequest(_, RequestInfoMatch(expected_request_info)))
@@ -1233,7 +1244,7 @@
 
   // Submit the form using JavaScript.
   WebStatePolicyDecider::RequestInfo form_request_info(
-      ui::PageTransition::PAGE_TRANSITION_FORM_SUBMIT,
+      ui::PageTransition::PAGE_TRANSITION_FORM_SUBMIT, url,
       /*target_main_frame=*/true, /*has_user_gesture=*/false);
   EXPECT_CALL(*decider_,
               ShouldAllowRequest(_, RequestInfoMatch(form_request_info)))
@@ -1254,19 +1265,19 @@
       WaitForWebViewContainingText(web_state(), testing::kTestFormFieldValue));
 
   // Go Back.
-  WebStatePolicyDecider::RequestInfo forward_back_request_info(
-      ui::PageTransition::PAGE_TRANSITION_FORWARD_BACK,
+  WebStatePolicyDecider::RequestInfo back_request_info(
+      ui::PageTransition::PAGE_TRANSITION_FORWARD_BACK, url,
       /*target_main_frame=*/true, /*has_user_gesture=*/false);
   if (GetWebClient()->IsSlimNavigationManagerEnabled()) {
     EXPECT_CALL(observer_, DidChangeBackForwardState(web_state())).Times(2);
-    EXPECT_CALL(*decider_, ShouldAllowRequest(
-                               _, RequestInfoMatch(forward_back_request_info)))
+    EXPECT_CALL(*decider_,
+                ShouldAllowRequest(_, RequestInfoMatch(back_request_info)))
         .WillOnce(Return(true));
     EXPECT_CALL(observer_, DidStartLoading(web_state()));
   } else {
     EXPECT_CALL(observer_, DidStartLoading(web_state()));
-    EXPECT_CALL(*decider_, ShouldAllowRequest(
-                               _, RequestInfoMatch(forward_back_request_info)))
+    EXPECT_CALL(*decider_,
+                ShouldAllowRequest(_, RequestInfoMatch(back_request_info)))
         .WillOnce(Return(true));
   }
 
@@ -1286,6 +1297,9 @@
   }));
 
   // Go forward.
+  WebStatePolicyDecider::RequestInfo forward_request_info(
+      ui::PageTransition::PAGE_TRANSITION_FORWARD_BACK, action,
+      /*target_main_frame=*/true, /*has_user_gesture=*/false);
   NavigationContext* context = nullptr;
   int32_t nav_id = 0;
   if (GetWebClient()->IsSlimNavigationManagerEnabled()) {
@@ -1294,8 +1308,8 @@
     EXPECT_CALL(observer_, DidStartLoading(web_state()));
   } else {
     EXPECT_CALL(observer_, DidStartLoading(web_state()));
-    EXPECT_CALL(*decider_, ShouldAllowRequest(
-                               _, RequestInfoMatch(forward_back_request_info)))
+    EXPECT_CALL(*decider_,
+                ShouldAllowRequest(_, RequestInfoMatch(forward_request_info)))
         .WillOnce(Return(true));
   }
   EXPECT_CALL(observer_, DidStartNavigation(web_state(), _))
@@ -1329,7 +1343,7 @@
   int32_t nav_id = 0;
   EXPECT_CALL(observer_, DidStartLoading(web_state()));
   WebStatePolicyDecider::RequestInfo expected_request_info(
-      ui::PageTransition::PAGE_TRANSITION_CLIENT_REDIRECT,
+      ui::PageTransition::PAGE_TRANSITION_CLIENT_REDIRECT, url,
       /*target_main_frame=*/true, /*has_user_gesture=*/false);
   EXPECT_CALL(*decider_,
               ShouldAllowRequest(_, RequestInfoMatch(expected_request_info)))
@@ -1362,7 +1376,7 @@
   int32_t nav_id = 0;
   EXPECT_CALL(observer_, DidStartLoading(web_state()));
   WebStatePolicyDecider::RequestInfo expected_request_info(
-      ui::PageTransition::PAGE_TRANSITION_CLIENT_REDIRECT,
+      ui::PageTransition::PAGE_TRANSITION_CLIENT_REDIRECT, url,
       /*target_main_frame=*/true, /*has_user_gesture=*/false);
   EXPECT_CALL(*decider_,
               ShouldAllowRequest(_, RequestInfoMatch(expected_request_info)))
@@ -1396,7 +1410,7 @@
   int32_t nav_id = 0;
   EXPECT_CALL(observer_, DidStartLoading(web_state()));
   WebStatePolicyDecider::RequestInfo expected_request_info(
-      ui::PageTransition::PAGE_TRANSITION_CLIENT_REDIRECT,
+      ui::PageTransition::PAGE_TRANSITION_CLIENT_REDIRECT, url,
       /*target_main_frame=*/true, /*has_user_gesture=*/false);
   EXPECT_CALL(*decider_,
               ShouldAllowRequest(_, RequestInfoMatch(expected_request_info)))
@@ -1432,6 +1446,7 @@
   EXPECT_CALL(observer_, DidStartLoading(web_state()));
   WebStatePolicyDecider::RequestInfo expected_request_info(
       ui::PageTransition::PAGE_TRANSITION_CLIENT_REDIRECT,
+      test_server_->GetURL("/echo"),
       /*target_main_frame=*/true, /*has_user_gesture=*/false);
   EXPECT_CALL(*decider_,
               ShouldAllowRequest(_, RequestInfoMatch(expected_request_info)))
@@ -1451,7 +1466,7 @@
   int32_t nav_id = 0;
   EXPECT_CALL(observer_, DidStartLoading(web_state()));
   WebStatePolicyDecider::RequestInfo expected_request_info(
-      ui::PageTransition::PAGE_TRANSITION_CLIENT_REDIRECT,
+      ui::PageTransition::PAGE_TRANSITION_CLIENT_REDIRECT, url,
       /*target_main_frame=*/true, /*has_user_gesture=*/false);
   EXPECT_CALL(*decider_,
               ShouldAllowRequest(_, RequestInfoMatch(expected_request_info)))
@@ -1477,6 +1492,7 @@
   EXPECT_CALL(observer_, DidStopLoading(web_state()));
   WebStatePolicyDecider::RequestInfo expected_request_info(
       ui::PageTransition::PAGE_TRANSITION_CLIENT_REDIRECT,
+      /*source_url=*/GURL::EmptyGURL(),
       /*target_main_frame=*/true, /*has_user_gesture=*/false);
   EXPECT_CALL(*decider_,
               ShouldAllowRequest(_, RequestInfoMatch(expected_request_info)))
@@ -1494,7 +1510,7 @@
   int32_t nav_id = 0;
   EXPECT_CALL(observer_, DidStartLoading(web_state()));
   WebStatePolicyDecider::RequestInfo expected_request_info(
-      ui::PageTransition::PAGE_TRANSITION_CLIENT_REDIRECT,
+      ui::PageTransition::PAGE_TRANSITION_CLIENT_REDIRECT, url,
       /*target_main_frame=*/true, /*has_user_gesture=*/false);
   EXPECT_CALL(*decider_,
               ShouldAllowRequest(_, RequestInfoMatch(expected_request_info)))
@@ -1533,7 +1549,7 @@
   // Callbacks due to loading of the main frame.
   EXPECT_CALL(observer_, DidStartLoading(web_state()));
   WebStatePolicyDecider::RequestInfo expected_request_info(
-      ui::PageTransition::PAGE_TRANSITION_CLIENT_REDIRECT,
+      ui::PageTransition::PAGE_TRANSITION_CLIENT_REDIRECT, url,
       /*target_main_frame=*/true, /*has_user_gesture=*/false);
   EXPECT_CALL(*decider_,
               ShouldAllowRequest(_, RequestInfoMatch(expected_request_info)))
@@ -1544,7 +1560,7 @@
   EXPECT_CALL(observer_, DidFinishNavigation(web_state(), _));
   // Callbacks due to initial loading of iframe.
   WebStatePolicyDecider::RequestInfo iframe_request_info(
-      ui::PageTransition::PAGE_TRANSITION_CLIENT_REDIRECT,
+      ui::PageTransition::PAGE_TRANSITION_CLIENT_REDIRECT, url,
       /*target_main_frame=*/false, /*has_user_gesture=*/true);
   EXPECT_CALL(*decider_,
               ShouldAllowRequest(_, RequestInfoMatch(iframe_request_info)))
diff --git a/ios/web/web_state/ui/crw_web_controller.mm b/ios/web/web_state/ui/crw_web_controller.mm
index 4a579bf..4c44ef3 100644
--- a/ios/web/web_state/ui/crw_web_controller.mm
+++ b/ios/web/web_state/ui/crw_web_controller.mm
@@ -330,10 +330,6 @@
   // does not own this pointer.
   WebStateImpl* _webStateImpl;
 
-  // A set of URLs opened in external applications; stored so that errors
-  // from the web view can be identified as resulting from these events.
-  NSMutableSet* _openedApplicationURL;
-
   // A set of script managers whose scripts have been injected into the current
   // page.
   // TODO(stuartmorgan): Revisit this approach; it's intended only as a stopgap
@@ -1555,9 +1551,6 @@
 
 // Load the current URL in a web view, first ensuring the web view is visible.
 - (void)loadCurrentURLInWebView {
-  // Clear the set of URLs opened in external applications.
-  _openedApplicationURL = [[NSMutableSet alloc] init];
-
   web::NavigationItem* item = self.currentNavItem;
   GURL targetURL = item ? item->GetVirtualURL() : GURL::EmptyGURL();
   // Load the url. The UIWebView delegate callbacks take care of updating the
@@ -2913,7 +2906,8 @@
       return NO;
     }
     web::NavigationItem* item = self.currentNavItem;
-    GURL sourceURL = item ? item->GetOriginalRequestURL() : GURL::EmptyGURL();
+    const GURL& sourceURL =
+        item ? item->GetOriginalRequestURL() : GURL::EmptyGURL();
 
     // Stop load if navigation is believed to be happening on the main frame.
     if ([self isMainFrameNavigationAction:action])
@@ -2944,9 +2938,6 @@
     if ([_delegate openExternalURL:requestURL
                          sourceURL:sourceURL
                        linkClicked:isNavigationTypeLinkActivated]) {
-      // Record the URL so that errors reported following the 'NO' reply can be
-      // safely ignored.
-      [_openedApplicationURL addObject:request.URL];
       if ([self shouldClosePageOnNativeApplicationLoad])
         _webStateImpl->CloseWebState();
     }
@@ -3032,12 +3023,6 @@
         }
       }
 
-      if ([_openedApplicationURL containsObject:errorURL]) {
-        // The load was rejected, because embedder launched an external
-        // application.
-        return;
-      }
-
       if (!navigationContext->IsDownload()) {
         // Non-download navigation was cancelled because WKWebView has opened a
         // Universal Link and called webView:didFailProvisionalNavigation:.
@@ -4307,9 +4292,12 @@
       [self userClickedRecently] &&
       net::GURLWithNSURL(action.request.mainDocumentURL) ==
           _lastUserInteraction->main_document_url;
+  web::NavigationItem* item = self.currentNavItem;
+  const GURL& sourceURL =
+      item ? item->GetOriginalRequestURL() : GURL::EmptyGURL();
 
   web::WebStatePolicyDecider::RequestInfo requestInfo(
-      transition, [self isMainFrameNavigationAction:action],
+      transition, sourceURL, [self isMainFrameNavigationAction:action],
       userInteractedWithRequestMainFrame);
   BOOL allowLoad =
       self.webStateImpl->ShouldAllowRequest(action.request, requestInfo);
diff --git a/ios/web/web_state/web_state_impl_unittest.mm b/ios/web/web_state/web_state_impl_unittest.mm
index 30dafe7..9305fea 100644
--- a/ios/web/web_state/web_state_impl_unittest.mm
+++ b/ios/web/web_state/web_state_impl_unittest.mm
@@ -711,6 +711,7 @@
 MATCHER_P(RequestInfoMatch, expected_request_info, /* argument_name = */ "") {
   return ui::PageTransitionTypeIncludingQualifiersIs(
              arg.transition_type, expected_request_info.transition_type) &&
+         arg.source_url == expected_request_info.source_url &&
          arg.target_frame_is_main ==
              expected_request_info.target_frame_is_main &&
          arg.has_user_gesture == expected_request_info.has_user_gesture;
@@ -731,7 +732,8 @@
 
   // Test that ShouldAllowRequest() is called for the same parameters.
   WebStatePolicyDecider::RequestInfo request_info_main_frame(
-      ui::PageTransition::PAGE_TRANSITION_LINK, /*target_main_frame=*/true,
+      ui::PageTransition::PAGE_TRANSITION_LINK,
+      /*source_url=*/GURL::EmptyGURL(), /*target_main_frame=*/true,
       /*has_user_gesture=*/false);
   EXPECT_CALL(decider, ShouldAllowRequest(
                            request, RequestInfoMatch(request_info_main_frame)))
@@ -745,7 +747,8 @@
   EXPECT_TRUE(web_state_->ShouldAllowRequest(request, request_info_main_frame));
 
   WebStatePolicyDecider::RequestInfo request_info_iframe(
-      ui::PageTransition::PAGE_TRANSITION_LINK, /*target_main_frame=*/false,
+      ui::PageTransition::PAGE_TRANSITION_LINK,
+      /*source_url=*/GURL::EmptyGURL(), /*target_main_frame=*/false,
       /*has_user_gesture=*/false);
 
   EXPECT_CALL(decider, ShouldAllowRequest(
diff --git a/ios/web_view/BUILD.gn b/ios/web_view/BUILD.gn
index d48fedc..d8950f09 100644
--- a/ios/web_view/BUILD.gn
+++ b/ios/web_view/BUILD.gn
@@ -309,6 +309,18 @@
   "internal/translate/fake_web_view_translate_client.mm",
 ]
 
+source_set("run_all_unittests") {
+  testonly = true
+  sources = [
+    "test/run_all_unittests.cc",
+  ]
+  deps = [
+    "//base",
+    "//base/test:test_support",
+    "//mojo/edk",
+  ]
+}
+
 test("ios_web_view_unittests") {
   testonly = true
   sources = [
@@ -332,8 +344,8 @@
   sources += ios_web_view_test_sources
 
   deps = [
+    ":run_all_unittests",
     "test:test_support",
-    "//base/test:run_all_unittests",
     "//components/autofill/core/browser:test_support",
     "//components/autofill/ios/browser:test_support",
     "//components/prefs:test_support",
diff --git a/ios/web_view/internal/autofill/cwv_autofill_controller_unittest.mm b/ios/web_view/internal/autofill/cwv_autofill_controller_unittest.mm
index 51b0119..7a82799 100644
--- a/ios/web_view/internal/autofill/cwv_autofill_controller_unittest.mm
+++ b/ios/web_view/internal/autofill/cwv_autofill_controller_unittest.mm
@@ -18,6 +18,7 @@
 #import "ios/web/public/test/fakes/crw_test_js_injection_receiver.h"
 #import "ios/web/public/test/fakes/test_web_state.h"
 #include "ios/web/public/test/test_web_thread_bundle.h"
+#include "ios/web/public/web_client.h"
 #include "ios/web/public/web_state/form_activity_params.h"
 #import "ios/web_view/internal/autofill/cwv_autofill_suggestion_internal.h"
 #include "ios/web_view/internal/web_view_browser_state.h"
@@ -49,6 +50,7 @@
 class CWVAutofillControllerTest : public PlatformTest {
  protected:
   CWVAutofillControllerTest() : browser_state_(/*off_the_record=*/false) {
+    web::SetWebClient(&web_client_);
     l10n_util::OverrideLocaleWithCocoaLocale();
 
     web_state_.SetBrowserState(&browser_state_);
@@ -71,6 +73,7 @@
                                     JSSuggestionManager:js_suggestion_manager_];
   };
 
+  web::WebClient web_client_;
   web::TestWebThreadBundle web_thread_bundle_;
   ios_web_view::WebViewBrowserState browser_state_;
   web::TestWebState web_state_;
diff --git a/ios/web_view/internal/web_view_url_request_context_getter.mm b/ios/web_view/internal/web_view_url_request_context_getter.mm
index 2c365e7..16b3a38 100644
--- a/ios/web_view/internal/web_view_url_request_context_getter.mm
+++ b/ios/web_view/internal/web_view_url_request_context_getter.mm
@@ -87,8 +87,10 @@
         new net::CookieStoreIOSPersistent(persistent_store.get()));
     storage_->set_cookie_store(std::move(cookie_store));
 
+    web::WebClient* web_client = web::GetWebClient();
+    DCHECK(web_client);
     std::string user_agent =
-        web::GetWebClient()->GetUserAgent(web::UserAgentType::MOBILE);
+        web_client->GetUserAgent(web::UserAgentType::MOBILE);
 
     storage_->set_http_user_agent_settings(
         std::make_unique<net::StaticHttpUserAgentSettings>("en-us,en",
diff --git a/ios/web_view/test/DEPS b/ios/web_view/test/DEPS
index 4ccbc06..2f4c024e8 100644
--- a/ios/web_view/test/DEPS
+++ b/ios/web_view/test/DEPS
@@ -2,5 +2,6 @@
   "+base",
   "+ios/testing",
   "+ios/web_view/public",
+  "+mojo/edk/embedder",
   "+net",
 ]
diff --git a/ios/web_view/test/run_all_unittests.cc b/ios/web_view/test/run_all_unittests.cc
new file mode 100644
index 0000000..d7be30c
--- /dev/null
+++ b/ios/web_view/test/run_all_unittests.cc
@@ -0,0 +1,16 @@
+// 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 "base/bind.h"
+#include "base/test/launcher/unit_test_launcher.h"
+#include "base/test/test_suite.h"
+#include "mojo/edk/embedder/embedder.h"
+
+int main(int argc, char** argv) {
+  base::TestSuite test_suite(argc, argv);
+  mojo::edk::Init();
+  return base::LaunchUnitTests(
+      argc, argv,
+      base::BindOnce(&base::TestSuite::Run, base::Unretained(&test_suite)));
+}
diff --git a/media/capture/mojom/video_capture_types.mojom b/media/capture/mojom/video_capture_types.mojom
index d742942..6f79dd7 100644
--- a/media/capture/mojom/video_capture_types.mojom
+++ b/media/capture/mojom/video_capture_types.mojom
@@ -71,6 +71,18 @@
   OTHER_TRANSPORT
 };
 
+enum VideoCaptureBufferType {
+  kSharedMemory,
+
+  // Warning: This case is a workaround for compatibility with an older version
+  // of Mojo only and will be deleted as soon as the Mojo version of ChromiumOS
+  // becomes compatible with the |kSharedMemory|.
+  // TODO(chfremer): Remove this when https://crbug.com/857537 is resolved.
+  kSharedMemoryViaRawFileDescriptor,
+
+  kMailboxHolder
+};
+
 struct VideoCaptureFormat {
   gfx.mojom.Size frame_size;
   float frame_rate;
@@ -79,6 +91,7 @@
 
 struct VideoCaptureParams {
   VideoCaptureFormat requested_format;
+  VideoCaptureBufferType buffer_type;
   ResolutionChangePolicy resolution_change_policy;
   PowerLineFrequency power_line_frequency;
 };
@@ -117,8 +130,14 @@
   array<gpu.mojom.MailboxHolder, 4> mailbox_holder;
 };
 
+struct SharedMemoryViaRawFileDescriptor {
+  handle file_descriptor_handle;
+  uint32 shared_memory_size_in_bytes;
+};
+
 union VideoBufferHandle {
   handle<shared_buffer> shared_buffer_handle;
+  SharedMemoryViaRawFileDescriptor shared_memory_via_raw_file_descriptor;
   MailboxBufferHandleSet mailbox_handles;
 };
 
diff --git a/media/capture/mojom/video_capture_types.typemap b/media/capture/mojom/video_capture_types.typemap
index 27b8599..cec8543c 100644
--- a/media/capture/mojom/video_capture_types.typemap
+++ b/media/capture/mojom/video_capture_types.typemap
@@ -33,6 +33,7 @@
   "media.mojom.ResolutionChangePolicy=media::ResolutionChangePolicy",
   "media.mojom.PowerLineFrequency=media::PowerLineFrequency",
   "media.mojom.VideoCapturePixelFormat=media::VideoPixelFormat",
+  "media.mojom.VideoCaptureBufferType=media::VideoCaptureBufferType",
   "media.mojom.VideoCaptureFormat=media::VideoCaptureFormat",
   "media.mojom.VideoCaptureParams=media::VideoCaptureParams",
   "media.mojom.VideoCaptureDeviceDescriptorCameraCalibration=media::VideoCaptureDeviceDescriptor::CameraCalibration",
diff --git a/media/capture/mojom/video_capture_types_mojom_traits.cc b/media/capture/mojom/video_capture_types_mojom_traits.cc
index 36d75419..d9356887 100644
--- a/media/capture/mojom/video_capture_types_mojom_traits.cc
+++ b/media/capture/mojom/video_capture_types_mojom_traits.cc
@@ -234,6 +234,46 @@
 }
 
 // static
+media::mojom::VideoCaptureBufferType
+EnumTraits<media::mojom::VideoCaptureBufferType,
+           media::VideoCaptureBufferType>::ToMojom(media::VideoCaptureBufferType
+                                                       input) {
+  switch (input) {
+    case media::VideoCaptureBufferType::kSharedMemory:
+      return media::mojom::VideoCaptureBufferType::kSharedMemory;
+    case media::VideoCaptureBufferType::kSharedMemoryViaRawFileDescriptor:
+      return media::mojom::VideoCaptureBufferType::
+          kSharedMemoryViaRawFileDescriptor;
+    case media::VideoCaptureBufferType::kMailboxHolder:
+      return media::mojom::VideoCaptureBufferType::kMailboxHolder;
+  }
+  NOTREACHED();
+  return media::mojom::VideoCaptureBufferType::kSharedMemory;
+}
+
+// static
+bool EnumTraits<media::mojom::VideoCaptureBufferType,
+                media::VideoCaptureBufferType>::
+    FromMojom(media::mojom::VideoCaptureBufferType input,
+              media::VideoCaptureBufferType* output) {
+  switch (input) {
+    case media::mojom::VideoCaptureBufferType::kSharedMemory:
+      *output = media::VideoCaptureBufferType::kSharedMemory;
+      return true;
+    case media::mojom::VideoCaptureBufferType::
+        kSharedMemoryViaRawFileDescriptor:
+      *output =
+          media::VideoCaptureBufferType::kSharedMemoryViaRawFileDescriptor;
+      return true;
+    case media::mojom::VideoCaptureBufferType::kMailboxHolder:
+      *output = media::VideoCaptureBufferType::kMailboxHolder;
+      return true;
+  }
+  NOTREACHED();
+  return false;
+}
+
+// static
 media::mojom::VideoCaptureApi
 EnumTraits<media::mojom::VideoCaptureApi, media::VideoCaptureApi>::ToMojom(
     media::VideoCaptureApi input) {
@@ -365,6 +405,8 @@
          media::VideoCaptureParams* out) {
   if (!data.ReadRequestedFormat(&out->requested_format))
     return false;
+  if (!data.ReadBufferType(&out->buffer_type))
+    return false;
   if (!data.ReadResolutionChangePolicy(&out->resolution_change_policy))
     return false;
   if (!data.ReadPowerLineFrequency(&out->power_line_frequency))
diff --git a/media/capture/mojom/video_capture_types_mojom_traits.h b/media/capture/mojom/video_capture_types_mojom_traits.h
index 4c9283c4..3660c7a 100644
--- a/media/capture/mojom/video_capture_types_mojom_traits.h
+++ b/media/capture/mojom/video_capture_types_mojom_traits.h
@@ -41,6 +41,16 @@
 };
 
 template <>
+struct EnumTraits<media::mojom::VideoCaptureBufferType,
+                  media::VideoCaptureBufferType> {
+  static media::mojom::VideoCaptureBufferType ToMojom(
+      media::VideoCaptureBufferType buffer_type);
+
+  static bool FromMojom(media::mojom::VideoCaptureBufferType input,
+                        media::VideoCaptureBufferType* out);
+};
+
+template <>
 struct EnumTraits<media::mojom::VideoCaptureApi, media::VideoCaptureApi> {
   static media::mojom::VideoCaptureApi ToMojom(media::VideoCaptureApi input);
   static bool FromMojom(media::mojom::VideoCaptureApi input,
@@ -84,6 +94,11 @@
     return params.requested_format;
   }
 
+  static media::VideoCaptureBufferType buffer_type(
+      const media::VideoCaptureParams& params) {
+    return params.buffer_type;
+  }
+
   static media::ResolutionChangePolicy resolution_change_policy(
       const media::VideoCaptureParams& params) {
     return params.resolution_change_policy;
diff --git a/media/capture/video/fake_video_capture_device_unittest.cc b/media/capture/video/fake_video_capture_device_unittest.cc
index df3d606..66af0d49 100644
--- a/media/capture/video/fake_video_capture_device_unittest.cc
+++ b/media/capture/video/fake_video_capture_device_unittest.cc
@@ -68,6 +68,10 @@
     return base::SharedMemoryHandle();
   }
 
+  uint32_t GetMemorySizeInBytes() override {
+    return static_cast<uint32_t>(mapped_size_);
+  }
+
   std::unique_ptr<VideoCaptureBufferHandle> GetHandleForInProcessAccess()
       override {
     return std::make_unique<StubBufferHandle>(mapped_size_, data_);
diff --git a/media/capture/video/shared_memory_buffer_tracker.cc b/media/capture/video/shared_memory_buffer_tracker.cc
index 7cd4a05..e375a183 100644
--- a/media/capture/video/shared_memory_buffer_tracker.cc
+++ b/media/capture/video/shared_memory_buffer_tracker.cc
@@ -39,4 +39,8 @@
   return provider_.GetNonOwnedSharedMemoryHandleForLegacyIPC();
 }
 
+uint32_t SharedMemoryBufferTracker::GetMemorySizeInBytes() {
+  return provider_.GetMemorySizeInBytes();
+}
+
 }  // namespace media
diff --git a/media/capture/video/shared_memory_buffer_tracker.h b/media/capture/video/shared_memory_buffer_tracker.h
index 2c92ca0..92abef7 100644
--- a/media/capture/video/shared_memory_buffer_tracker.h
+++ b/media/capture/video/shared_memory_buffer_tracker.h
@@ -27,6 +27,7 @@
   std::unique_ptr<VideoCaptureBufferHandle> GetMemoryMappedAccess() override;
   mojo::ScopedSharedBufferHandle GetHandleForTransit(bool read_only) override;
   base::SharedMemoryHandle GetNonOwnedSharedMemoryHandleForLegacyIPC() override;
+  uint32_t GetMemorySizeInBytes() override;
 
  private:
   SharedMemoryHandleProvider provider_;
diff --git a/media/capture/video/shared_memory_handle_provider.cc b/media/capture/video/shared_memory_handle_provider.cc
index 72a90933..2e2e7e78 100644
--- a/media/capture/video/shared_memory_handle_provider.cc
+++ b/media/capture/video/shared_memory_handle_provider.cc
@@ -63,6 +63,25 @@
   return true;
 }
 
+#if defined(OS_LINUX)
+bool SharedMemoryHandleProvider::InitAsReadOnlyFromRawFileDescriptor(
+    mojo::ScopedHandle fd_handle,
+    uint32_t memory_size_in_bytes) {
+  base::PlatformFile platform_file;
+  const MojoResult result =
+      mojo::UnwrapPlatformFile(std::move(fd_handle), &platform_file);
+  if (result != MOJO_RESULT_OK)
+    return false;
+  base::UnguessableToken guid = base::UnguessableToken::Create();
+  base::SharedMemoryHandle memory_handle(
+      base::FileDescriptor(platform_file, true), 0u, guid);
+  mapped_size_ = memory_size_in_bytes;
+  read_only_flag_ = true;
+  shared_memory_.emplace(memory_handle, read_only_flag_);
+  return true;
+}
+#endif  // defined(OS_LINUX)
+
 mojo::ScopedSharedBufferHandle
 SharedMemoryHandleProvider::GetHandleForInterProcessTransit(bool read_only) {
   if (read_only_flag_ && !read_only) {
@@ -88,6 +107,10 @@
   return shared_memory_->handle();
 }
 
+uint32_t SharedMemoryHandleProvider::GetMemorySizeInBytes() {
+  return static_cast<uint32_t>(mapped_size_);
+}
+
 std::unique_ptr<VideoCaptureBufferHandle>
 SharedMemoryHandleProvider::GetHandleForInProcessAccess() {
   {
diff --git a/media/capture/video/shared_memory_handle_provider.h b/media/capture/video/shared_memory_handle_provider.h
index 8a14f3a..3a46afa 100644
--- a/media/capture/video/shared_memory_handle_provider.h
+++ b/media/capture/video/shared_memory_handle_provider.h
@@ -10,6 +10,7 @@
 #include "base/logging.h"
 #include "base/memory/shared_memory.h"
 #include "base/optional.h"
+#include "build/build_config.h"
 #include "media/capture/capture_export.h"
 #include "media/capture/video/video_capture_buffer_handle.h"
 #include "media/capture/video/video_capture_device.h"
@@ -35,10 +36,18 @@
   // if the operation failed.
   bool InitFromMojoHandle(mojo::ScopedSharedBufferHandle buffer_handle);
 
+// This requires platforms where base::SharedMemoryHandle is backed by a
+// file descriptor.
+#if defined(OS_LINUX)
+  bool InitAsReadOnlyFromRawFileDescriptor(mojo::ScopedHandle fd_handle,
+                                           uint32_t memory_size_in_bytes);
+#endif  // defined(OS_LINUX)
+
   // Implementation of Buffer::HandleProvider:
   mojo::ScopedSharedBufferHandle GetHandleForInterProcessTransit(
       bool read_only) override;
   base::SharedMemoryHandle GetNonOwnedSharedMemoryHandleForLegacyIPC() override;
+  uint32_t GetMemorySizeInBytes() override;
   std::unique_ptr<VideoCaptureBufferHandle> GetHandleForInProcessAccess()
       override;
 
diff --git a/media/capture/video/video_capture_buffer_pool.h b/media/capture/video/video_capture_buffer_pool.h
index 1760aea..93f5435 100644
--- a/media/capture/video/video_capture_buffer_pool.h
+++ b/media/capture/video/video_capture_buffer_pool.h
@@ -50,6 +50,8 @@
   virtual base::SharedMemoryHandle GetNonOwnedSharedMemoryHandleForLegacyIPC(
       int buffer_id) = 0;
 
+  virtual uint32_t GetMemorySizeInBytes(int buffer_id) = 0;
+
   // Try and obtain a read/write access to the buffer.
   virtual std::unique_ptr<VideoCaptureBufferHandle> GetHandleForInProcessAccess(
       int buffer_id) = 0;
diff --git a/media/capture/video/video_capture_buffer_pool_impl.cc b/media/capture/video/video_capture_buffer_pool_impl.cc
index 477f5697..921d74d 100644
--- a/media/capture/video/video_capture_buffer_pool_impl.cc
+++ b/media/capture/video/video_capture_buffer_pool_impl.cc
@@ -53,6 +53,18 @@
   return tracker->GetNonOwnedSharedMemoryHandleForLegacyIPC();
 }
 
+uint32_t VideoCaptureBufferPoolImpl::GetMemorySizeInBytes(int buffer_id) {
+  base::AutoLock lock(lock_);
+
+  VideoCaptureBufferTracker* tracker = GetTracker(buffer_id);
+  if (!tracker) {
+    NOTREACHED() << "Invalid buffer_id.";
+    return 0u;
+  }
+
+  return tracker->GetMemorySizeInBytes();
+}
+
 std::unique_ptr<VideoCaptureBufferHandle>
 VideoCaptureBufferPoolImpl::GetHandleForInProcessAccess(int buffer_id) {
   base::AutoLock lock(lock_);
diff --git a/media/capture/video/video_capture_buffer_pool_impl.h b/media/capture/video/video_capture_buffer_pool_impl.h
index 34b585f0..e317a38 100644
--- a/media/capture/video/video_capture_buffer_pool_impl.h
+++ b/media/capture/video/video_capture_buffer_pool_impl.h
@@ -40,6 +40,7 @@
       bool read_only) override;
   base::SharedMemoryHandle GetNonOwnedSharedMemoryHandleForLegacyIPC(
       int buffer_id) override;
+  uint32_t GetMemorySizeInBytes(int buffer_id) override;
   std::unique_ptr<VideoCaptureBufferHandle> GetHandleForInProcessAccess(
       int buffer_id) override;
   int ReserveForProducer(const gfx::Size& dimensions,
diff --git a/media/capture/video/video_capture_buffer_tracker.h b/media/capture/video/video_capture_buffer_tracker.h
index 040ff53..a4f7dff 100644
--- a/media/capture/video/video_capture_buffer_tracker.h
+++ b/media/capture/video/video_capture_buffer_tracker.h
@@ -44,6 +44,7 @@
       bool read_only) = 0;
   virtual base::SharedMemoryHandle
   GetNonOwnedSharedMemoryHandleForLegacyIPC() = 0;
+  virtual uint32_t GetMemorySizeInBytes() = 0;
 
  private:
   // |dimensions_| may change as a VideoCaptureBufferTracker is re-used, but
diff --git a/media/capture/video/video_capture_device.h b/media/capture/video/video_capture_device.h
index a2642ba..43f7f699 100644
--- a/media/capture/video/video_capture_device.h
+++ b/media/capture/video/video_capture_device.h
@@ -99,6 +99,7 @@
             bool read_only) = 0;
         virtual base::SharedMemoryHandle
         GetNonOwnedSharedMemoryHandleForLegacyIPC() = 0;
+        virtual uint32_t GetMemorySizeInBytes() = 0;
         virtual std::unique_ptr<VideoCaptureBufferHandle>
         GetHandleForInProcessAccess() = 0;
       };
diff --git a/media/capture/video/video_capture_device_client.cc b/media/capture/video/video_capture_device_client.cc
index 4512819..75a50db 100644
--- a/media/capture/video/video_capture_device_client.cc
+++ b/media/capture/video/video_capture_device_client.cc
@@ -22,6 +22,7 @@
 #include "media/capture/video/video_capture_jpeg_decoder.h"
 #include "media/capture/video/video_frame_receiver.h"
 #include "media/capture/video_capture_types.h"
+#include "mojo/public/cpp/system/platform_handle.h"
 #include "third_party/libyuv/include/libyuv.h"
 
 namespace {
@@ -86,6 +87,9 @@
       override {
     return buffer_pool_->GetNonOwnedSharedMemoryHandleForLegacyIPC(buffer_id_);
   }
+  uint32_t GetMemorySizeInBytes() override {
+    return buffer_pool_->GetMemorySizeInBytes(buffer_id_);
+  }
   std::unique_ptr<VideoCaptureBufferHandle> GetHandleForInProcessAccess()
       override {
     return buffer_pool_->GetHandleForInProcessAccess(buffer_id_);
@@ -97,10 +101,12 @@
 };
 
 VideoCaptureDeviceClient::VideoCaptureDeviceClient(
+    VideoCaptureBufferType target_buffer_type,
     std::unique_ptr<VideoFrameReceiver> receiver,
     scoped_refptr<VideoCaptureBufferPool> buffer_pool,
     VideoCaptureJpegDecoderFactoryCB optional_jpeg_decoder_factory_callback)
-    : receiver_(std::move(receiver)),
+    : target_buffer_type_(target_buffer_type),
+      receiver_(std::move(receiver)),
       optional_jpeg_decoder_factory_callback_(
           std::move(optional_jpeg_decoder_factory_callback)),
       buffer_pool_(std::move(buffer_pool)),
@@ -402,9 +408,20 @@
   if (!base::ContainsValue(buffer_ids_known_by_receiver_, buffer_id)) {
     media::mojom::VideoBufferHandlePtr buffer_handle =
         media::mojom::VideoBufferHandle::New();
-    buffer_handle->set_shared_buffer_handle(
-        buffer_pool_->GetHandleForInterProcessTransit(buffer_id,
-                                                      true /*read_only*/));
+    switch (target_buffer_type_) {
+      case VideoCaptureBufferType::kSharedMemory:
+        buffer_handle->set_shared_buffer_handle(
+            buffer_pool_->GetHandleForInterProcessTransit(buffer_id,
+                                                          true /*read_only*/));
+        break;
+      case VideoCaptureBufferType::kSharedMemoryViaRawFileDescriptor:
+        buffer_handle->set_shared_memory_via_raw_file_descriptor(
+            CreateSharedMemoryViaRawFileDescriptorStruct(buffer_id));
+        break;
+      case VideoCaptureBufferType::kMailboxHolder:
+        NOTREACHED();
+        break;
+    }
     receiver_->OnNewBuffer(buffer_id, std::move(buffer_handle));
     buffer_ids_known_by_receiver_.push_back(buffer_id);
   }
@@ -412,6 +429,26 @@
   return MakeBufferStruct(buffer_pool_, buffer_id, frame_feedback_id);
 }
 
+mojom::SharedMemoryViaRawFileDescriptorPtr
+VideoCaptureDeviceClient::CreateSharedMemoryViaRawFileDescriptorStruct(
+    int buffer_id) {
+// This requires platforms where base::SharedMemoryHandle is backed by a
+// file descriptor.
+#if defined(OS_LINUX)
+  auto result = mojom::SharedMemoryViaRawFileDescriptor::New();
+  result->file_descriptor_handle = mojo::WrapPlatformFile(
+      base::SharedMemory::DuplicateHandle(
+          buffer_pool_->GetNonOwnedSharedMemoryHandleForLegacyIPC(buffer_id))
+          .GetHandle());
+  result->shared_memory_size_in_bytes =
+      buffer_pool_->GetMemorySizeInBytes(buffer_id);
+  return result;
+#else
+  NOTREACHED();
+  return mojom::SharedMemoryViaRawFileDescriptorPtr();
+#endif
+}
+
 void VideoCaptureDeviceClient::OnIncomingCapturedBuffer(
     Buffer buffer,
     const VideoCaptureFormat& format,
diff --git a/media/capture/video/video_capture_device_client.h b/media/capture/video/video_capture_device_client.h
index 778d46a1..73509d0 100644
--- a/media/capture/video/video_capture_device_client.h
+++ b/media/capture/video/video_capture_device_client.h
@@ -15,6 +15,7 @@
 #include "base/memory/ref_counted.h"
 #include "base/threading/thread_collision_warner.h"
 #include "media/capture/capture_export.h"
+#include "media/capture/mojom/video_capture_types.mojom.h"
 #include "media/capture/video/video_capture_device.h"
 
 namespace media {
@@ -43,6 +44,7 @@
     : public VideoCaptureDevice::Client {
  public:
   VideoCaptureDeviceClient(
+      VideoCaptureBufferType target_buffer_type,
       std::unique_ptr<VideoFrameReceiver> receiver,
       scoped_refptr<VideoCaptureBufferPool> buffer_pool,
       VideoCaptureJpegDecoderFactoryCB optional_jpeg_decoder_factory_callback);
@@ -99,6 +101,11 @@
                                  base::TimeDelta timestamp,
                                  int frame_feedback_id);
 
+  mojom::SharedMemoryViaRawFileDescriptorPtr
+  CreateSharedMemoryViaRawFileDescriptorStruct(int buffer_id);
+
+  const VideoCaptureBufferType target_buffer_type_;
+
   // The receiver to which we post events.
   const std::unique_ptr<VideoFrameReceiver> receiver_;
   std::vector<int> buffer_ids_known_by_receiver_;
diff --git a/media/capture/video/video_capture_device_client_unittest.cc b/media/capture/video/video_capture_device_client_unittest.cc
index a3467e7..56ada33 100644
--- a/media/capture/video/video_capture_device_client_unittest.cc
+++ b/media/capture/video/video_capture_device_client_unittest.cc
@@ -54,8 +54,8 @@
     gpu_memory_buffer_manager_ =
         std::make_unique<unittest_internal::MockGpuMemoryBufferManager>();
     device_client_ = std::make_unique<VideoCaptureDeviceClient>(
-        std::move(controller), buffer_pool,
-        base::Bind(&ReturnNullPtrAsJpecDecoder));
+        VideoCaptureBufferType::kSharedMemory, std::move(controller),
+        buffer_pool, base::BindRepeating(&ReturnNullPtrAsJpecDecoder));
   }
   ~VideoCaptureDeviceClientTest() override = default;
 
diff --git a/media/capture/video_capture_types.cc b/media/capture/video_capture_types.cc
index b6fc66e..6cd3063b 100644
--- a/media/capture/video_capture_types.cc
+++ b/media/capture/video_capture_types.cc
@@ -70,7 +70,8 @@
 }
 
 VideoCaptureParams::VideoCaptureParams()
-    : resolution_change_policy(ResolutionChangePolicy::FIXED_RESOLUTION),
+    : buffer_type(VideoCaptureBufferType::kSharedMemory),
+      resolution_change_policy(ResolutionChangePolicy::FIXED_RESOLUTION),
       power_line_frequency(PowerLineFrequency::FREQUENCY_DEFAULT) {}
 
 bool VideoCaptureParams::IsValid() const {
diff --git a/media/capture/video_capture_types.h b/media/capture/video_capture_types.h
index bb43a31..cc7a5c42 100644
--- a/media/capture/video_capture_types.h
+++ b/media/capture/video_capture_types.h
@@ -54,6 +54,12 @@
   FREQUENCY_MAX = FREQUENCY_60HZ
 };
 
+enum class VideoCaptureBufferType {
+  kSharedMemory,
+  kSharedMemoryViaRawFileDescriptor,
+  kMailboxHolder
+};
+
 // Assert that the int:frequency mapping is correct.
 static_assert(static_cast<int>(PowerLineFrequency::FREQUENCY_DEFAULT) == 0,
               "static_cast<int>(FREQUENCY_DEFAULT) must equal 0.");
@@ -135,6 +141,8 @@
   // Requests a resolution and format at which the capture will occur.
   VideoCaptureFormat requested_format;
 
+  VideoCaptureBufferType buffer_type;
+
   // Policy for resolution change.
   ResolutionChangePolicy resolution_change_policy;
 
diff --git a/media/mojo/interfaces/video_decoder_config_struct_traits.cc b/media/mojo/interfaces/video_decoder_config_struct_traits.cc
index fc0bd489..c414581 100644
--- a/media/mojo/interfaces/video_decoder_config_struct_traits.cc
+++ b/media/mojo/interfaces/video_decoder_config_struct_traits.cc
@@ -73,4 +73,4 @@
   return true;
 }
 
-}  // namespace mojo
\ No newline at end of file
+}  // namespace mojo
diff --git a/net/dns/host_resolver_impl.cc b/net/dns/host_resolver_impl.cc
index f7dd23d..8d60955 100644
--- a/net/dns/host_resolver_impl.cc
+++ b/net/dns/host_resolver_impl.cc
@@ -683,28 +683,27 @@
 //
 // TODO(szym): Move to separate source file for testing and mocking.
 //
-class HostResolverImpl::ProcTask
-    : public base::RefCountedThreadSafe<HostResolverImpl::ProcTask> {
+class HostResolverImpl::ProcTask {
  public:
-  typedef base::Callback<void(int net_error,
-                              const AddressList& addr_list)> Callback;
+  typedef base::OnceCallback<void(int net_error, const AddressList& addr_list)>
+      Callback;
 
   ProcTask(const Key& key,
            const ProcTaskParams& params,
-           const Callback& callback,
+           Callback callback,
            scoped_refptr<base::TaskRunner> proc_task_runner,
            const NetLogWithSource& job_net_log,
            const base::TickClock* tick_clock)
       : key_(key),
         params_(params),
-        callback_(callback),
+        callback_(std::move(callback)),
         network_task_runner_(base::ThreadTaskRunnerHandle::Get()),
         proc_task_runner_(std::move(proc_task_runner)),
         attempt_number_(0),
-        completed_attempt_number_(0),
-        completed_attempt_error_(ERR_UNEXPECTED),
         net_log_(job_net_log),
-        tick_clock_(tick_clock) {
+        tick_clock_(tick_clock),
+        weak_ptr_factory_(this) {
+    DCHECK(callback_);
     if (!params_.resolver_proc.get())
       params_.resolver_proc = HostResolverProc::GetDefault();
     // If default is unset, use the system proc.
@@ -712,47 +711,47 @@
       params_.resolver_proc = new SystemHostResolverProc();
   }
 
+  // Cancels this ProcTask. Any outstanding resolve attempts running on worker
+  // thread will continue running, but they will post back to the network thread
+  // before checking their WeakPtrs to find that this task is cancelled.
+  ~ProcTask() {
+    DCHECK(network_task_runner_->BelongsToCurrentThread());
+
+    // If this is cancellation, log the EndEvent (otherwise this was logged in
+    // OnLookupComplete()).
+    if (!was_completed())
+      net_log_.EndEvent(NetLogEventType::HOST_RESOLVER_IMPL_PROC_TASK);
+  }
+
   void Start() {
     DCHECK(network_task_runner_->BelongsToCurrentThread());
+    DCHECK(!was_completed());
     net_log_.BeginEvent(NetLogEventType::HOST_RESOLVER_IMPL_PROC_TASK);
     StartLookupAttempt();
   }
 
-  // Cancels this ProcTask. It will be orphaned. Any outstanding resolve
-  // attempts running on worker thread will continue running. Only once all the
-  // attempts complete will the final reference to this ProcTask be released.
-  void Cancel() {
-    DCHECK(network_task_runner_->BelongsToCurrentThread());
-
-    if (was_canceled() || was_completed())
-      return;
-
-    callback_.Reset();
-    net_log_.EndEvent(NetLogEventType::HOST_RESOLVER_IMPL_PROC_TASK);
-  }
-
-  bool was_canceled() const {
+  bool was_completed() const {
     DCHECK(network_task_runner_->BelongsToCurrentThread());
     return callback_.is_null();
   }
 
-  bool was_completed() const {
-    DCHECK(network_task_runner_->BelongsToCurrentThread());
-    return completed_attempt_number_ > 0;
-  }
-
  private:
-  friend class base::RefCountedThreadSafe<ProcTask>;
-  ~ProcTask() = default;
+  using AttemptCompletionCallback = base::OnceCallback<
+      void(const AddressList& results, int error, const int os_error)>;
 
   void StartLookupAttempt() {
     DCHECK(network_task_runner_->BelongsToCurrentThread());
+    DCHECK(!was_completed());
     base::TimeTicks start_time = tick_clock_->NowTicks();
     ++attempt_number_;
     // Dispatch the lookup attempt to a worker thread.
+    AttemptCompletionCallback completion_callback = base::BindOnce(
+        &ProcTask::OnLookupAttemptComplete, weak_ptr_factory_.GetWeakPtr(),
+        start_time, attempt_number_, tick_clock_);
     proc_task_runner_->PostTask(
         FROM_HERE,
-        base::Bind(&ProcTask::DoLookup, this, start_time, attempt_number_));
+        base::BindOnce(&ProcTask::DoLookup, key_, params_.resolver_proc,
+                       network_task_runner_, std::move(completion_callback)));
 
     net_log_.AddEvent(NetLogEventType::HOST_RESOLVER_IMPL_ATTEMPT_STARTED,
                       NetLog::IntCallback("attempt_number", attempt_number_));
@@ -760,179 +759,148 @@
     // If the results aren't received within a given time, RetryIfNotComplete
     // will start a new attempt if none of the outstanding attempts have
     // completed yet.
+    // Use a WeakPtr to avoid keeping the ProcTask alive after completion or
+    // cancellation.
     if (attempt_number_ <= params_.max_retry_attempts) {
       network_task_runner_->PostDelayedTask(
-          FROM_HERE, base::Bind(&ProcTask::RetryIfNotComplete, this),
-          params_.unresponsive_delay);
+          FROM_HERE,
+          base::BindOnce(&ProcTask::StartLookupAttempt,
+                         weak_ptr_factory_.GetWeakPtr()),
+          params_.unresponsive_delay *
+              std::pow(params_.retry_factor, attempt_number_ - 1));
     }
   }
 
   // WARNING: This code runs in TaskScheduler with CONTINUE_ON_SHUTDOWN. The
   // shutdown code cannot wait for it to finish, so this code must be very
   // careful about using other objects (like MessageLoops, Singletons, etc).
-  // During shutdown these objects may no longer exist. Multiple DoLookups()
-  // could be running in parallel, so any state inside of |this| must not
-  // mutate.
-  void DoLookup(const base::TimeTicks& start_time,
-                const uint32_t attempt_number) {
+  // During shutdown these objects may no longer exist.
+  static void DoLookup(
+      Key key,
+      scoped_refptr<HostResolverProc> resolver_proc,
+      scoped_refptr<base::SingleThreadTaskRunner> network_task_runner,
+      AttemptCompletionCallback completion_callback) {
     AddressList results;
     int os_error = 0;
-    int error = params_.resolver_proc->Resolve(key_.hostname,
-                                               key_.address_family,
-                                               key_.host_resolver_flags,
-                                               &results,
-                                               &os_error);
+    int error =
+        resolver_proc->Resolve(key.hostname, key.address_family,
+                               key.host_resolver_flags, &results, &os_error);
 
-    network_task_runner_->PostTask(
-        FROM_HERE, base::Bind(&ProcTask::OnLookupComplete, this, results,
-                              start_time, attempt_number, error, os_error));
+    network_task_runner->PostTask(
+        FROM_HERE, base::BindOnce(std::move(completion_callback), results,
+                                  error, os_error));
   }
 
-  // Makes next attempt if DoLookup() has not finished.
-  void RetryIfNotComplete() {
-    DCHECK(network_task_runner_->BelongsToCurrentThread());
-
-    if (was_completed() || was_canceled())
-      return;
-
-    params_.unresponsive_delay *= params_.retry_factor;
-    StartLookupAttempt();
-  }
-
-  // Callback for when DoLookup() completes (runs on task runner thread).
-  void OnLookupComplete(const AddressList& results,
-                        const base::TimeTicks& start_time,
-                        const uint32_t attempt_number,
-                        int error,
-                        const int os_error) {
+  // Callback for when DoLookup() completes (runs on task runner thread). Now
+  // that we're back in the network thread, checks that |proc_task| is still
+  // valid, and if so, passes back to the object.
+  static void OnLookupAttemptComplete(base::WeakPtr<ProcTask> proc_task,
+                                      const base::TimeTicks& start_time,
+                                      const uint32_t attempt_number,
+                                      const base::TickClock* tick_clock,
+                                      const AddressList& results,
+                                      int error,
+                                      const int os_error) {
     TRACE_EVENT0(kNetTracingCategory, "ProcTask::OnLookupComplete");
-    DCHECK(network_task_runner_->BelongsToCurrentThread());
+
     // If results are empty, we should return an error.
     bool empty_list_on_ok = (error == OK && results.empty());
     if (empty_list_on_ok)
       error = ERR_NAME_NOT_RESOLVED;
 
-    bool was_retry_attempt = attempt_number > 1;
-
     // Ideally the following code would be part of host_resolver_proc.cc,
     // however it isn't safe to call NetworkChangeNotifier from worker threads.
     // So do it here on the IO thread instead.
     if (error != OK && NetworkChangeNotifier::IsOffline())
       error = ERR_INTERNET_DISCONNECTED;
 
-    RecordAttemptHistograms(start_time, attempt_number, error, os_error);
+    RecordAttemptHistograms(start_time, attempt_number, error, os_error,
+                            tick_clock);
 
-    if (was_canceled())
+    if (!proc_task) {
+      RecordDiscardedAttemptHistograms(attempt_number);
       return;
+    }
+
+    proc_task->OnLookupComplete(results, start_time, attempt_number, error,
+                                os_error);
+  }
+
+  void OnLookupComplete(const AddressList& results,
+                        const base::TimeTicks& start_time,
+                        const uint32_t attempt_number,
+                        int error,
+                        const int os_error) {
+    DCHECK(network_task_runner_->BelongsToCurrentThread());
+    DCHECK(!was_completed());
+
+    // Invalidate WeakPtrs to cancel handling of all outstanding lookup attempts
+    // and retries.
+    weak_ptr_factory_.InvalidateWeakPtrs();
+
+    RecordTaskHistograms(start_time, error, os_error, attempt_number);
 
     NetLogParametersCallback net_log_callback;
-    if (error != OK) {
-      net_log_callback = base::Bind(&NetLogProcTaskFailedCallback,
-                                    attempt_number,
-                                    error,
-                                    os_error);
-    } else {
-      net_log_callback = NetLog::IntCallback("attempt_number", attempt_number);
-    }
-    net_log_.AddEvent(NetLogEventType::HOST_RESOLVER_IMPL_ATTEMPT_FINISHED,
-                      net_log_callback);
-
-    if (was_completed())
-      return;
-
-    RecordTaskHistograms(start_time, error, os_error);
-
-    // Copy the results from the first worker thread that resolves the host.
-    results_ = results;
-    completed_attempt_number_ = attempt_number;
-    completed_attempt_error_ = error;
-
-    if (was_retry_attempt) {
-      // If retry attempt finishes before 1st attempt, then get stats on how
-      // much time is saved by having spawned an extra attempt.
-      retry_attempt_finished_time_ = tick_clock_->NowTicks();
-    }
-
+    NetLogParametersCallback attempt_net_log_callback;
     if (error != OK) {
       net_log_callback = base::Bind(&NetLogProcTaskFailedCallback,
                                     0, error, os_error);
+      attempt_net_log_callback = base::Bind(&NetLogProcTaskFailedCallback,
+                                            attempt_number, error, os_error);
     } else {
-      net_log_callback = results_.CreateNetLogCallback();
+      net_log_callback = results.CreateNetLogCallback();
+      attempt_net_log_callback =
+          NetLog::IntCallback("attempt_number", attempt_number);
     }
     net_log_.EndEvent(NetLogEventType::HOST_RESOLVER_IMPL_PROC_TASK,
                       net_log_callback);
+    net_log_.AddEvent(NetLogEventType::HOST_RESOLVER_IMPL_ATTEMPT_FINISHED,
+                      attempt_net_log_callback);
 
-    callback_.Run(error, results_);
+    std::move(callback_).Run(error, results);
   }
 
   void RecordTaskHistograms(const base::TimeTicks& start_time,
                             const int error,
-                            const int os_error) const {
+                            const int os_error,
+                            const uint32_t attempt_number) const {
     DCHECK(network_task_runner_->BelongsToCurrentThread());
     base::TimeDelta duration = tick_clock_->NowTicks() - start_time;
-    if (error == OK)
+    if (error == OK) {
       UMA_HISTOGRAM_LONG_TIMES_100("Net.DNS.ProcTask.SuccessTime", duration);
-    else
+      UMA_HISTOGRAM_ENUMERATION("DNS.AttemptFirstSuccess", attempt_number, 100);
+    } else {
       UMA_HISTOGRAM_LONG_TIMES_100("Net.DNS.ProcTask.FailureTime", duration);
+      UMA_HISTOGRAM_ENUMERATION("DNS.AttemptFirstFailure", attempt_number, 100);
+    }
 
     UMA_HISTOGRAM_CUSTOM_ENUMERATION(kOSErrorsForGetAddrinfoHistogramName,
                                      std::abs(os_error),
                                      GetAllGetAddrinfoOSErrors());
   }
 
-  void RecordAttemptHistograms(const base::TimeTicks& start_time,
-                               const uint32_t attempt_number,
-                               const int error,
-                               const int os_error) const {
-    DCHECK(network_task_runner_->BelongsToCurrentThread());
-    bool first_attempt_to_complete =
-        completed_attempt_number_ == attempt_number;
-    bool is_first_attempt = (attempt_number == 1);
-
-    if (first_attempt_to_complete) {
-      // If this was first attempt to complete, then record the resolution
-      // status of the attempt.
-      if (completed_attempt_error_ == OK) {
-        UMA_HISTOGRAM_ENUMERATION(
-            "DNS.AttemptFirstSuccess", attempt_number, 100);
-      } else {
-        UMA_HISTOGRAM_ENUMERATION(
-            "DNS.AttemptFirstFailure", attempt_number, 100);
-      }
-    }
-
-    if (error == OK)
+  static void RecordAttemptHistograms(const base::TimeTicks& start_time,
+                                      const uint32_t attempt_number,
+                                      const int error,
+                                      const int os_error,
+                                      const base::TickClock* tick_clock) {
+    base::TimeDelta duration = tick_clock->NowTicks() - start_time;
+    if (error == OK) {
       UMA_HISTOGRAM_ENUMERATION("DNS.AttemptSuccess", attempt_number, 100);
-    else
-      UMA_HISTOGRAM_ENUMERATION("DNS.AttemptFailure", attempt_number, 100);
-
-    // If first attempt didn't finish before retry attempt, then calculate stats
-    // on how much time is saved by having spawned an extra attempt.
-    if (!first_attempt_to_complete && is_first_attempt && !was_canceled()) {
-      UMA_HISTOGRAM_LONG_TIMES_100(
-          "DNS.AttemptTimeSavedByRetry",
-          tick_clock_->NowTicks() - retry_attempt_finished_time_);
-    }
-
-    if (was_canceled() || !first_attempt_to_complete) {
-      // Count those attempts which completed after the job was already canceled
-      // OR after the job was already completed by an earlier attempt (so in
-      // effect).
-      UMA_HISTOGRAM_ENUMERATION("DNS.AttemptDiscarded", attempt_number, 100);
-
-      // Record if job is canceled.
-      if (was_canceled())
-        UMA_HISTOGRAM_ENUMERATION("DNS.AttemptCancelled", attempt_number, 100);
-    }
-
-    base::TimeDelta duration = tick_clock_->NowTicks() - start_time;
-    if (error == OK)
       UMA_HISTOGRAM_LONG_TIMES_100("DNS.AttemptSuccessDuration", duration);
-    else
+    } else {
+      UMA_HISTOGRAM_ENUMERATION("DNS.AttemptFailure", attempt_number, 100);
       UMA_HISTOGRAM_LONG_TIMES_100("DNS.AttemptFailDuration", duration);
+    }
   }
 
-  // Set on the task runner thread, read on the worker thread.
+  static void RecordDiscardedAttemptHistograms(const uint32_t attempt_number) {
+    // Count those attempts which completed after the job was already canceled
+    // OR after the job was already completed by an earlier attempt (so
+    // cancelled in effect).
+    UMA_HISTOGRAM_ENUMERATION("DNS.AttemptDiscarded", attempt_number, 100);
+  }
+
   Key key_;
 
   // Holds an owning reference to the HostResolverProc that we are going to use.
@@ -954,22 +922,15 @@
   // number.
   uint32_t attempt_number_;
 
-  // The index of the attempt which finished first (or 0 if the job is still in
-  // progress).
-  uint32_t completed_attempt_number_;
-
-  // The result (a net error code) from the first attempt to complete.
-  int completed_attempt_error_;
-
-  // The time when retry attempt was finished.
-  base::TimeTicks retry_attempt_finished_time_;
-
-  AddressList results_;
-
   NetLogWithSource net_log_;
 
   const base::TickClock* tick_clock_;
 
+  // Used to loop back from the blocking lookup attempt tasks as well as from
+  // delayed retry tasks. Invalidate WeakPtrs on completion and cancellation to
+  // cancel handling of such posted tasks.
+  base::WeakPtrFactory<ProcTask> weak_ptr_factory_;
+
   DISALLOW_COPY_AND_ASSIGN(ProcTask);
 };
 
@@ -1259,10 +1220,7 @@
     if (is_running()) {
       // |resolver_| was destroyed with this Job still in flight.
       // Clean-up, record in the log, but don't run any callbacks.
-      if (is_proc_running()) {
-        proc_task_->Cancel();
-        proc_task_ = nullptr;
-      }
+      proc_task_ = nullptr;
       // Clean up now for nice NetLog.
       KillDnsTask();
       net_log_.EndEventWithNetErrorCode(NetLogEventType::HOST_RESOLVER_IMPL_JOB,
@@ -1521,7 +1479,7 @@
   // PrioritizedDispatcher with tighter limits.
   void StartProcTask() {
     DCHECK(!is_dns_running());
-    proc_task_ = new ProcTask(
+    proc_task_ = std::make_unique<ProcTask>(
         key_, resolver_->proc_params_,
         base::Bind(&Job::OnProcTaskComplete, base::Unretained(this),
                    tick_clock_->NowTicks()),
@@ -1769,11 +1727,7 @@
     std::unique_ptr<Job> self_deleter = resolver_->RemoveJob(this);
 
     if (is_running()) {
-      if (is_proc_running()) {
-        DCHECK(!is_queued());
-        proc_task_->Cancel();
-        proc_task_ = nullptr;
-      }
+      proc_task_ = nullptr;
       KillDnsTask();
 
       // Signal dispatcher that a slot has opened.
@@ -1880,7 +1834,7 @@
   NetLogWithSource net_log_;
 
   // Resolves the host using a HostResolverProc.
-  scoped_refptr<ProcTask> proc_task_;
+  std::unique_ptr<ProcTask> proc_task_;
 
   // Resolves the host using a DnsTransaction.
   std::unique_ptr<DnsTask> dns_task_;
diff --git a/net/dns/host_resolver_impl_unittest.cc b/net/dns/host_resolver_impl_unittest.cc
index 800a21c..ad44f6f 100644
--- a/net/dns/host_resolver_impl_unittest.cc
+++ b/net/dns/host_resolver_impl_unittest.cc
@@ -20,6 +20,7 @@
 #include "base/strings/stringprintf.h"
 #include "base/synchronization/condition_variable.h"
 #include "base/synchronization/lock.h"
+#include "base/test/test_mock_time_task_runner.h"
 #include "base/test/test_timeouts.h"
 #include "base/threading/thread_restrictions.h"
 #include "base/threading/thread_task_runner_handle.h"
@@ -359,32 +360,34 @@
         total_attempts_(total_attempts),
         total_attempts_resolved_(0),
         resolved_attempt_number_(0),
-        all_done_(&lock_) {
-  }
+        num_attempts_waiting_(0),
+        all_done_(&lock_),
+        blocked_attempt_signal_(&lock_) {}
 
   // Test harness will wait for all attempts to finish before checking the
   // results.
-  void WaitForAllAttemptsToFinish(const base::TimeDelta& wait_time) {
-    base::TimeTicks end_time = base::TimeTicks::Now() + wait_time;
-    {
-      base::AutoLock auto_lock(lock_);
-      while (total_attempts_resolved_ != total_attempts_ &&
-          base::TimeTicks::Now() < end_time) {
-        all_done_.TimedWait(end_time - base::TimeTicks::Now());
-      }
+  void WaitForAllAttemptsToFinish() {
+    base::AutoLock auto_lock(lock_);
+    while (total_attempts_resolved_ != total_attempts_) {
+      all_done_.Wait();
+    }
+  }
+
+  void WaitForNAttemptsToBeBlocked(int n) {
+    base::AutoLock auto_lock(lock_);
+    while (num_attempts_waiting_ < n) {
+      blocked_attempt_signal_.Wait();
     }
   }
 
   // All attempts will wait for an attempt to resolve the host.
   void WaitForAnAttemptToComplete() {
-    base::TimeDelta wait_time = base::TimeDelta::FromSeconds(60);
-    base::TimeTicks end_time = base::TimeTicks::Now() + wait_time;
     {
       base::AutoLock auto_lock(lock_);
       base::ScopedAllowBaseSyncPrimitivesForTesting
           scoped_allow_base_sync_primitives;
-      while (resolved_attempt_number_ == 0 && base::TimeTicks::Now() < end_time)
-        all_done_.TimedWait(end_time - base::TimeTicks::Now());
+      while (resolved_attempt_number_ == 0)
+        all_done_.Wait();
     }
     all_done_.Broadcast();  // Tell all waiting attempts to proceed.
   }
@@ -395,6 +398,9 @@
   // Returns the first attempt that that has resolved the host.
   int resolved_attempt_number() { return resolved_attempt_number_; }
 
+  // Returns the current number of blocked attempts.
+  int num_attempts_waiting() { return num_attempts_waiting_; }
+
   // HostResolverProc methods.
   int Resolve(const std::string& host,
               AddressFamily address_family,
@@ -405,12 +411,15 @@
     {
       base::AutoLock auto_lock(lock_);
       ++current_attempt_number_;
+      ++num_attempts_waiting_;
       if (current_attempt_number_ == attempt_number_to_resolve_) {
         resolved_attempt_number_ = current_attempt_number_;
         wait_for_right_attempt_to_complete = false;
       }
     }
 
+    blocked_attempt_signal_.Broadcast();
+
     if (wait_for_right_attempt_to_complete)
       // Wait for the attempt_number_to_resolve_ attempt to resolve.
       WaitForAnAttemptToComplete();
@@ -421,6 +430,7 @@
     {
       base::AutoLock auto_lock(lock_);
       ++total_attempts_resolved_;
+      --num_attempts_waiting_;
     }
 
     all_done_.Broadcast();  // Tell all attempts to proceed.
@@ -444,10 +454,12 @@
   int total_attempts_;
   int total_attempts_resolved_;
   int resolved_attempt_number_;
+  int num_attempts_waiting_;
 
   // All attempts wait for right attempt to be resolve.
   base::Lock lock_;
   base::ConditionVariable all_done_;
+  base::ConditionVariable blocked_attempt_signal_;
 };
 
 // TestHostResolverImpl's sole purpose is to mock the IPv6 reachability test.
@@ -1516,37 +1528,58 @@
 // Test the retry attempts simulating host resolver proc that takes too long.
 TEST_F(HostResolverImplTest, MultipleAttempts) {
   // Total number of attempts would be 3 and we want the 3rd attempt to resolve
-  // the host. First and second attempt will be forced to sleep until they get
+  // the host. First and second attempt will be forced to wait until they get
   // word that a resolution has completed. The 3rd resolution attempt will try
-  // to get done ASAP, and won't sleep..
+  // to get done ASAP, and won't wait.
   int kAttemptNumberToResolve = 3;
   int kTotalAttempts = 3;
 
+  // Add a little bit of extra fudge to the delay to allow reasonable
+  // flexibility for time > vs >= etc.  We don't need to fail the test if we
+  // retry at t=6001 instead of t=6000.
+  base::TimeDelta kSleepFudgeFactor = base::TimeDelta::FromMilliseconds(1);
+
   scoped_refptr<LookupAttemptHostResolverProc> resolver_proc(
       new LookupAttemptHostResolverProc(
           NULL, kAttemptNumberToResolve, kTotalAttempts));
 
   HostResolverImpl::ProcTaskParams params = DefaultParams(resolver_proc.get());
-
-  // Specify smaller interval for unresponsive_delay_ for HostResolverImpl so
-  // that unit test runs faster. For example, this test finishes in 1.5 secs
-  // (500ms * 3).
-  params.unresponsive_delay = base::TimeDelta::FromMilliseconds(500);
+  base::TimeDelta unresponsive_delay = params.unresponsive_delay;
+  int retry_factor = params.retry_factor;
 
   resolver_.reset(new TestHostResolverImpl(DefaultOptions(), NULL));
   resolver_->set_proc_params_for_test(params);
 
+  // Override the current thread task runner, so we can simulate the passage of
+  // time and avoid any actual sleeps.
+  auto test_task_runner = base::MakeRefCounted<base::TestMockTimeTaskRunner>();
+  base::ScopedClosureRunner task_runner_override_scoped_cleanup =
+      base::ThreadTaskRunnerHandle::OverrideForTesting(test_task_runner);
+
   // Resolve "host1".
   HostResolver::RequestInfo info(HostPortPair("host1", 70));
   Request* req = CreateRequest(info, DEFAULT_PRIORITY);
   EXPECT_THAT(req->Resolve(), IsError(ERR_IO_PENDING));
 
+  resolver_proc->WaitForNAttemptsToBeBlocked(1);
+
+  test_task_runner->FastForwardBy(unresponsive_delay + kSleepFudgeFactor);
+  resolver_proc->WaitForNAttemptsToBeBlocked(2);
+
+  test_task_runner->FastForwardBy(unresponsive_delay * retry_factor +
+                                  kSleepFudgeFactor);
+
+  resolver_proc->WaitForAllAttemptsToFinish();
+  test_task_runner->RunUntilIdle();
+
   // Resolve returns -4 to indicate that 3rd attempt has resolved the host.
+  // Since we're using a TestMockTimeTaskRunner, the RunLoop stuff in
+  // WaitForResult will fail if it actually has to wait, but unless there's an
+  // error, the result should be immediately ready by this point.
   EXPECT_EQ(-4, req->WaitForResult());
 
-  resolver_proc->WaitForAllAttemptsToFinish(
-      base::TimeDelta::FromMilliseconds(60000));
-  base::RunLoop().RunUntilIdle();
+  // We should be done with retries, but make sure none erroneously happen.
+  test_task_runner->FastForwardUntilNoTasksRemain();
 
   EXPECT_EQ(resolver_proc->total_attempts_resolved(), kTotalAttempts);
   EXPECT_EQ(resolver_proc->resolved_attempt_number(), kAttemptNumberToResolve);
diff --git a/services/video_capture/device_media_to_mojo_adapter.cc b/services/video_capture/device_media_to_mojo_adapter.cc
index 8b5b60d..16f23c8a 100644
--- a/services/video_capture/device_media_to_mojo_adapter.cc
+++ b/services/video_capture/device_media_to_mojo_adapter.cc
@@ -59,6 +59,15 @@
   auto media_receiver = std::make_unique<media::VideoFrameReceiverOnTaskRunner>(
       receiver_->GetWeakPtr(), base::ThreadTaskRunnerHandle::Get());
 
+  if (requested_settings.buffer_type !=
+          media::VideoCaptureBufferType::kSharedMemory &&
+      requested_settings.buffer_type !=
+          media::VideoCaptureBufferType::kSharedMemoryViaRawFileDescriptor) {
+    // Buffer types other than shared memory are not supported.
+    media_receiver->OnError();
+    return;
+  }
+
   // Create a dedicated buffer pool for the device usage session.
   auto buffer_tracker_factory =
       std::make_unique<media::VideoCaptureBufferTrackerFactoryImpl>();
@@ -67,7 +76,7 @@
                                             max_buffer_pool_buffer_count()));
 
   auto device_client = std::make_unique<media::VideoCaptureDeviceClient>(
-      std::move(media_receiver), buffer_pool,
+      requested_settings.buffer_type, std::move(media_receiver), buffer_pool,
       base::BindRepeating(
           &CreateGpuJpegDecoder, jpeg_decoder_task_runner_,
           jpeg_decoder_factory_callback_,
diff --git a/services/video_capture/test/fake_device_unittest.cc b/services/video_capture/test/fake_device_unittest.cc
index 7037b8a..7e145d1 100644
--- a/services/video_capture/test/fake_device_unittest.cc
+++ b/services/video_capture/test/fake_device_unittest.cc
@@ -4,7 +4,9 @@
 
 #include "base/memory/ref_counted.h"
 #include "base/run_loop.h"
+#include "build/build_config.h"
 #include "media/base/video_frame.h"
+#include "media/capture/video/shared_memory_handle_provider.h"
 #include "media/mojo/common/media_type_converters.h"
 #include "services/video_capture/device_media_to_mojo_adapter.h"
 #include "services/video_capture/public/mojom/constants.mojom.h"
@@ -76,4 +78,74 @@
   ASSERT_LE(num_buffers_created, kMaxBufferPoolBuffers);
 }
 
+// This requires platforms where base::SharedMemoryHandle is backed by a
+// file descriptor.
+#if defined(OS_LINUX)
+TEST_F(FakeVideoCaptureDeviceTest,
+       ReceiveFramesViaFileDescriptorHandlesForSharedMemory) {
+  base::RunLoop wait_loop;
+  static const int kNumFramesToWaitFor = 3;
+  int num_frames_arrived = 0;
+  std::map<int32_t, std::unique_ptr<media::SharedMemoryHandleProvider>>
+      buffers_by_id;
+  mojom::ReceiverPtr receiver_proxy;
+  MockReceiver receiver(mojo::MakeRequest(&receiver_proxy));
+  EXPECT_CALL(receiver, DoOnNewBuffer(_, _))
+      .Times(AtLeast(1))
+      .WillRepeatedly(Invoke(
+          [&buffers_by_id](int32_t buffer_id,
+                           media::mojom::VideoBufferHandlePtr* buffer_handle) {
+            ASSERT_TRUE(
+                (*buffer_handle)->is_shared_memory_via_raw_file_descriptor());
+            auto provider =
+                std::make_unique<media::SharedMemoryHandleProvider>();
+            provider->InitAsReadOnlyFromRawFileDescriptor(
+                std::move((*buffer_handle)
+                              ->get_shared_memory_via_raw_file_descriptor()
+                              ->file_descriptor_handle),
+                (*buffer_handle)
+                    ->get_shared_memory_via_raw_file_descriptor()
+                    ->shared_memory_size_in_bytes);
+            buffers_by_id.insert(
+                std::make_pair(buffer_id, std::move(provider)));
+          }));
+  bool found_unexpected_all_zero_frame = false;
+  EXPECT_CALL(receiver, DoOnFrameReadyInBuffer(_, _, _, _))
+      .WillRepeatedly(Invoke([&wait_loop, &num_frames_arrived, &buffers_by_id,
+                              &found_unexpected_all_zero_frame](
+                                 int32_t buffer_id, int32_t frame_feedback_id,
+                                 mojom::ScopedAccessPermissionPtr*,
+                                 media::mojom::VideoFrameInfoPtr*) {
+        auto buffer_access =
+            buffers_by_id[buffer_id]->GetHandleForInProcessAccess();
+        // Check that there is at least one non-zero byte in the frame data.
+        bool found_non_zero_byte = false;
+        for (uint32_t i = 0; i < buffer_access->mapped_size(); i++) {
+          if (buffer_access->const_data()[i] != 0u) {
+            found_non_zero_byte = true;
+            break;
+          }
+        }
+        if (!found_non_zero_byte) {
+          found_unexpected_all_zero_frame = true;
+          wait_loop.Quit();
+          return;
+        }
+        num_frames_arrived += 1;
+        if (num_frames_arrived >= kNumFramesToWaitFor) {
+          wait_loop.Quit();
+        }
+      }));
+
+  // Make a copy of |requestable_settings_| and change it to ask for
+  // |kSharedMemoryViaRawFileDescriptor|.
+  media::VideoCaptureParams settings_to_request = requestable_settings_;
+  settings_to_request.buffer_type =
+      media::VideoCaptureBufferType::kSharedMemoryViaRawFileDescriptor;
+  fake_device_proxy_->Start(settings_to_request, std::move(receiver_proxy));
+  wait_loop.Run();
+  EXPECT_FALSE(found_unexpected_all_zero_frame);
+}
+#endif  // defined(OS_LINUX)
+
 }  // namespace video_capture
diff --git a/skia/BUILD.gn b/skia/BUILD.gn
index 775130c..eadcb12 100644
--- a/skia/BUILD.gn
+++ b/skia/BUILD.gn
@@ -481,6 +481,13 @@
       "//third_party/skia/src/ports/SkFontMgr_custom.cpp",
       "//third_party/skia/src/ports/SkFontMgr_custom_empty.cpp",
       "ext/fontmgr_default_fuchsia.cc",
+      "ext/fontmgr_default_fuchsia.h",
+      "ext/fontmgr_fuchsia.cc",
+      "ext/fontmgr_fuchsia.h",
+    ]
+    deps += [
+      "//third_party/fuchsia-sdk:fonts",
+      "//third_party/icu:icuuc",
     ]
   }
 
@@ -795,6 +802,17 @@
       "//skia/public/interfaces:test_interfaces",
     ]
   }
+
+  if (is_fuchsia) {
+    sources += [ "ext/fontmgr_fuchsia_unittest.cc" ]
+    deps += [
+      "//third_party/fuchsia-sdk:fonts",
+      "//third_party/test_fonts",
+    ]
+    data_deps = [
+      "//third_party/test_fonts",
+    ]
+  }
 }
 
 if (!is_ios) {
diff --git a/skia/config/SkUserConfig.h b/skia/config/SkUserConfig.h
index 2a2373d..27b53a79 100644
--- a/skia/config/SkUserConfig.h
+++ b/skia/config/SkUserConfig.h
@@ -193,8 +193,6 @@
 #define SK_SUPPORT_LEGACY_VULKAN_INTERFACE
 #endif
 
-#define SK_SUPPORT_LEGACY_LAZY_IMAGE_DECODE_BEHAVIOR
-
 ///////////////////////// Imported from BUILD.gn and skia_common.gypi
 
 /* In some places Skia can use static initializers for global initialization,
diff --git a/skia/ext/fontmgr_default_fuchsia.cc b/skia/ext/fontmgr_default_fuchsia.cc
index 582e5a2c..a7b5548 100644
--- a/skia/ext/fontmgr_default_fuchsia.cc
+++ b/skia/ext/fontmgr_default_fuchsia.cc
@@ -22,5 +22,7 @@
   if (g_default_fontmgr) {
     return sk_ref_sp(g_default_fontmgr);
   }
+  // TODO(crbug.com/800156): Replace with FuchsiaFontManager when we can
+  // access FontProvider in sandbox.
   return SkFontMgr_New_Custom_Empty();
 }
diff --git a/skia/ext/fontmgr_fuchsia.cc b/skia/ext/fontmgr_fuchsia.cc
new file mode 100644
index 0000000..6666cdb
--- /dev/null
+++ b/skia/ext/fontmgr_fuchsia.cc
@@ -0,0 +1,336 @@
+// 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 "skia/ext/fontmgr_fuchsia.h"
+
+#include <lib/zx/vmar.h>
+
+#include <unordered_map>
+
+#include "base/bind.h"
+#include "base/callback.h"
+#include "base/containers/small_map.h"
+#include "base/fuchsia/fuchsia_logging.h"
+#include "base/logging.h"
+#include "base/strings/string_util.h"
+#include "base/threading/thread_checker.h"
+#include "third_party/icu/source/common/unicode/uchar.h"
+#include "third_party/skia/src/core/SkFontDescriptor.h"
+#include "third_party/skia/src/core/SkMakeUnique.h"
+#include "third_party/skia/src/ports/SkFontHost_FreeType_common.h"
+#include "third_party/skia/src/ports/SkFontMgr_custom.h"
+
+namespace skia {
+
+namespace {
+
+constexpr char kDefaultFont[] = "Roboto";
+
+// Currently FontProvider doesn't support font aliases. The map below is used to
+// map common web fonts to font families that are expected to be present in
+// FontProvider.
+constexpr struct {
+  const char* font_name_in;
+  const char* font_name_out;
+} kFontMap[] = {{"sans", "Roboto"},
+                {"sans-serif", "Roboto"},
+                {"arial", "Roboto"},
+                {"helvetica", "Roboto"},
+                {"roboto", "Roboto"},
+                {"roboto slab", "RobotoSlab"},
+                {"serif", "RobotoSlab"},
+                {"georgia", "RobotoSlab"},
+                {"times", "RobotoSlab"},
+                {"times roman", "RobotoSlab"},
+                {"times new roman", "RobotoSlab"},
+                {"roboto mono", "RobotoMono"},
+                {"consolas", "RobotoMono"},
+                {"courier", "RobotoMono"},
+                {"courier new", "RobotoMono"},
+                {"monospace", "RobotoMono"}};
+
+fuchsia::fonts::FontSlant ToFontSlant(SkFontStyle::Slant slant) {
+  return (slant == SkFontStyle::kItalic_Slant)
+             ? fuchsia::fonts::FontSlant::ITALIC
+             : fuchsia::fonts::FontSlant::UPRIGHT;
+}
+
+void UnmapMemory(const void* buffer, void* context) {
+  const uintptr_t size = reinterpret_cast<uintptr_t>(context);
+  zx::vmar::root_self().unmap(reinterpret_cast<uintptr_t>(buffer), size);
+}
+
+sk_sp<SkData> BufferToSkData(fuchsia::mem::Buffer data) {
+  uintptr_t buffer = 0;
+  zx_status_t status = zx::vmar::root_self().map(0, data.vmo, 0, data.size,
+                                                 ZX_VM_FLAG_PERM_READ, &buffer);
+  if (status != ZX_OK) {
+    ZX_DLOG(ERROR, status) << "zx_vmar_map";
+    return nullptr;
+  }
+
+  return SkData::MakeWithProc(reinterpret_cast<void*>(buffer), data.size,
+                              UnmapMemory, reinterpret_cast<void*>(data.size));
+}
+
+// SkTypeface that notifies FontCache when it is being destroyed.
+class CachedTypeface : public SkTypeface_Stream {
+ public:
+  CachedTypeface(std::unique_ptr<SkFontData> font_data,
+                 const SkFontStyle& style,
+                 bool is_fixed_pitch,
+                 const SkString family_name,
+                 base::OnceClosure on_deleted)
+      : SkTypeface_Stream(std::move(font_data),
+                          style,
+                          is_fixed_pitch,
+                          /*sys_font=*/true,
+                          family_name),
+        on_deleted_(std::move(on_deleted)) {}
+
+  ~CachedTypeface() override {
+    if (on_deleted_)
+      std::move(on_deleted_).Run();
+  }
+
+ private:
+  base::OnceClosure on_deleted_;
+
+  DISALLOW_COPY_AND_ASSIGN(CachedTypeface);
+};
+
+sk_sp<SkTypeface> CreateTypefaceFromSkStream(
+    std::unique_ptr<SkStreamAsset> stream,
+    const SkFontArguments& args,
+    base::OnceClosure on_deleted) {
+  using Scanner = SkTypeface_FreeType::Scanner;
+  Scanner scanner;
+  bool is_fixed_pitch;
+  SkFontStyle style;
+  SkString name;
+  Scanner::AxisDefinitions axis_definitions;
+  if (!scanner.scanFont(stream.get(), args.getCollectionIndex(), &name, &style,
+                        &is_fixed_pitch, &axis_definitions)) {
+    return nullptr;
+  }
+
+  const SkFontArguments::VariationPosition position =
+      args.getVariationDesignPosition();
+  SkAutoSTMalloc<4, SkFixed> axis_values(axis_definitions.count());
+  Scanner::computeAxisValues(axis_definitions, position, axis_values, name);
+
+  auto font_data =
+      std::make_unique<SkFontData>(std::move(stream), args.getCollectionIndex(),
+                                   axis_values.get(), axis_definitions.count());
+  return sk_make_sp<CachedTypeface>(std::move(font_data), style, is_fixed_pitch,
+                                    name, std::move(on_deleted));
+}
+
+sk_sp<SkTypeface> CreateTypefaceFromFontData(fuchsia::fonts::FontData font_data,
+                                             base::OnceClosure on_deleted) {
+  sk_sp<SkData> data = BufferToSkData(std::move(font_data.buffer));
+  if (!data)
+    return nullptr;
+
+  // TODO(https://crbug.com/800156): Initialize font arguments with font index
+  // when font collection support is implemented in FontProvider.
+  SkFontArguments args;
+
+  return CreateTypefaceFromSkStream(
+      std::make_unique<SkMemoryStream>(std::move(data)), args,
+      std::move(on_deleted));
+}
+
+}  // namespace
+
+class FuchsiaFontManager::FontCache {
+ public:
+  FontCache();
+  ~FontCache();
+
+  sk_sp<SkTypeface> GetTypefaceFromFontData(fuchsia::fonts::FontData font_data);
+
+ private:
+  void OnTypefaceDeleted(zx_koid_t vmo_koid);
+
+  THREAD_CHECKER(thread_checker_);
+
+  // SkTypeface cache. They key is koid of the VMO that contains the typeface.
+  // This allows to reuse previously-created SkTypeface when FontProvider
+  // returns FontData with the same VMO.
+  base::small_map<std::unordered_map<zx_koid_t, SkTypeface*>> typefaces_;
+
+  base::WeakPtrFactory<FontCache> weak_factory_;
+
+  DISALLOW_COPY_AND_ASSIGN(FontCache);
+};
+
+FuchsiaFontManager::FontCache::FontCache() : weak_factory_(this) {}
+
+FuchsiaFontManager::FontCache::~FontCache() = default;
+
+sk_sp<SkTypeface> FuchsiaFontManager::FontCache::GetTypefaceFromFontData(
+    fuchsia::fonts::FontData font_data) {
+  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
+
+  zx_info_handle_basic_t vmo_info;
+  zx_status_t status = font_data.buffer.vmo.get_info(
+      ZX_INFO_HANDLE_BASIC, &vmo_info, sizeof(vmo_info), nullptr, nullptr);
+  if (status != ZX_OK) {
+    ZX_DLOG(ERROR, status) << "zx_object_get_info";
+    return nullptr;
+  }
+
+  sk_sp<SkTypeface> result;
+  SkTypeface** cached_typeface = &(typefaces_[vmo_info.koid]);
+  if (*cached_typeface) {
+    result = sk_ref_sp(*cached_typeface);
+  } else {
+    result = CreateTypefaceFromFontData(
+        std::move(font_data),
+        base::BindOnce(&FontCache::OnTypefaceDeleted,
+                       weak_factory_.GetWeakPtr(), vmo_info.koid));
+    *cached_typeface = result.get();
+  }
+  return result;
+}
+
+void FuchsiaFontManager::FontCache::OnTypefaceDeleted(zx_koid_t vmo_koid) {
+  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
+
+  bool was_found = typefaces_.erase(vmo_koid) != 0;
+  DCHECK(was_found);
+}
+
+FuchsiaFontManager::FuchsiaFontManager(
+    fuchsia::fonts::FontProviderSync2Ptr font_provider)
+    : font_provider_(std::move(font_provider)), font_cache_(new FontCache()) {
+  for (auto& m : kFontMap) {
+    font_map_[m.font_name_in] = m.font_name_out;
+  }
+
+  // Get default font.
+  default_typeface_.reset(onMatchFamilyStyle(kDefaultFont, SkFontStyle()));
+  if (!default_typeface_)
+    LOG(FATAL) << "Failed to get default font from the FontProvider.";
+}
+
+FuchsiaFontManager::~FuchsiaFontManager() = default;
+
+int FuchsiaFontManager::onCountFamilies() const {
+  NOTREACHED();
+  return 0;
+}
+
+void FuchsiaFontManager::onGetFamilyName(int index,
+                                         SkString* family_name) const {
+  NOTREACHED();
+}
+
+SkFontStyleSet* FuchsiaFontManager::onCreateStyleSet(int index) const {
+  NOTREACHED();
+  return nullptr;
+}
+
+SkFontStyleSet* FuchsiaFontManager::onMatchFamily(
+    const char family_name[]) const {
+  NOTREACHED();
+  return nullptr;
+}
+
+SkTypeface* FuchsiaFontManager::onMatchFamilyStyle(
+    const char family_name[],
+    const SkFontStyle& style) const {
+  std::string family_name_lowercase = base::ToLowerASCII(family_name);
+
+  fuchsia::fonts::FontRequest request;
+  auto it = font_map_.find(family_name_lowercase);
+  request.family = (it != font_map_.end()) ? it->second.c_str() : family_name;
+  request.weight = style.weight();
+  request.width = style.width();
+  request.slant = ToFontSlant(style.slant());
+
+  fuchsia::fonts::FontResponsePtr response;
+  auto status = font_provider_->GetFont(std::move(request), &response);
+  if (status.statvs != ZX_OK) {
+    ZX_DLOG(ERROR, status.statvs) << "Failed to query font provider.";
+  } else if (response) {
+    sk_sp<SkTypeface> result =
+        font_cache_->GetTypefaceFromFontData(std::move(response->data));
+    if (result)
+      return result.release();
+
+    LOG(ERROR) << "FontProvider returned invalid FontData for " << family_name;
+  }
+
+  // If Sans was requested and we failed to get a valid response from
+  // FontProvider then return |default_typeface_|. blink::FontCache queries Sans
+  // as a last-resort font. Returning |default_typeface_| here ensures that the
+  // renderer doesn't crash when FontProvider stops working.
+  if (family_name_lowercase == "sans") {
+    // Copy |default_typeface_| to increment ref-count before returning it.
+    sk_sp<SkTypeface> result = default_typeface_;
+    return result.release();
+  }
+
+  return nullptr;
+}
+
+SkTypeface* FuchsiaFontManager::onMatchFamilyStyleCharacter(
+    const char family_name[],
+    const SkFontStyle& style,
+    const char* bcp47[],
+    int bcp47Count,
+    SkUnichar character) const {
+  if (u_hasBinaryProperty(character, UCHAR_EMOJI))
+    return onMatchFamilyStyle("Noto Color Emoji", style);
+
+  return nullptr;
+}
+
+SkTypeface* FuchsiaFontManager::onMatchFaceStyle(
+    const SkTypeface* typeface,
+    const SkFontStyle& style) const {
+  NOTREACHED();
+  return nullptr;
+}
+
+sk_sp<SkTypeface> FuchsiaFontManager::onMakeFromData(sk_sp<SkData> data,
+                                                     int ttc_index) const {
+  return makeFromStream(std::make_unique<SkMemoryStream>(std::move(data)),
+                        ttc_index);
+}
+
+sk_sp<SkTypeface> FuchsiaFontManager::onMakeFromStreamIndex(
+    std::unique_ptr<SkStreamAsset> stream,
+    int ttc_index) const {
+  return makeFromStream(std::move(stream),
+                        SkFontArguments().setCollectionIndex(ttc_index));
+}
+
+sk_sp<SkTypeface> FuchsiaFontManager::onMakeFromStreamArgs(
+    std::unique_ptr<SkStreamAsset> stream,
+    const SkFontArguments& args) const {
+  return CreateTypefaceFromSkStream(std::move(stream), args,
+                                    /*on_deleted=*/base::OnceClosure());
+}
+
+sk_sp<SkTypeface> FuchsiaFontManager::onMakeFromFile(const char path[],
+                                                     int ttc_index) const {
+  NOTREACHED();
+  return nullptr;
+}
+
+sk_sp<SkTypeface> FuchsiaFontManager::onLegacyMakeTypeface(
+    const char family_name[],
+    SkFontStyle style) const {
+  sk_sp<SkTypeface> typeface;
+
+  if (family_name)
+    typeface.reset(this->onMatchFamilyStyle(family_name, style));
+
+  return typeface;
+}
+
+}  // namespace skia
diff --git a/skia/ext/fontmgr_fuchsia.h b/skia/ext/fontmgr_fuchsia.h
new file mode 100644
index 0000000..74ef8669e
--- /dev/null
+++ b/skia/ext/fontmgr_fuchsia.h
@@ -0,0 +1,71 @@
+// 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 SKIA_EXT_FONTMGR_FUCHSIA_H_
+#define SKIA_EXT_FONTMGR_FUCHSIA_H_
+
+#include <memory>
+
+#include <fuchsia/fonts/cpp/fidl.h>
+
+#include "base/containers/flat_map.h"
+#include "base/macros.h"
+#include "third_party/skia/include/core/SkFontMgr.h"
+#include "third_party/skia/include/core/SkStream.h"
+#include "third_party/skia/include/core/SkTypeface.h"
+
+namespace skia {
+
+class FuchsiaFontManager : public SkFontMgr {
+ public:
+  explicit FuchsiaFontManager(
+      fuchsia::fonts::FontProviderSync2Ptr font_provider);
+
+  ~FuchsiaFontManager() override;
+
+ protected:
+  // SkFontMgr overrides.
+  int onCountFamilies() const override;
+  void onGetFamilyName(int index, SkString* family_name) const override;
+  SkFontStyleSet* onMatchFamily(const char family_name[]) const override;
+  SkFontStyleSet* onCreateStyleSet(int index) const override;
+  SkTypeface* onMatchFamilyStyle(const char family_name[],
+                                 const SkFontStyle&) const override;
+  SkTypeface* onMatchFamilyStyleCharacter(const char family_name[],
+                                          const SkFontStyle&,
+                                          const char* bcp47[],
+                                          int bcp47_count,
+                                          SkUnichar character) const override;
+  SkTypeface* onMatchFaceStyle(const SkTypeface*,
+                               const SkFontStyle&) const override;
+  sk_sp<SkTypeface> onMakeFromData(sk_sp<SkData>, int ttc_index) const override;
+  sk_sp<SkTypeface> onMakeFromStreamIndex(std::unique_ptr<SkStreamAsset>,
+                                          int ttc_index) const override;
+  sk_sp<SkTypeface> onMakeFromStreamArgs(std::unique_ptr<SkStreamAsset>,
+                                         const SkFontArguments&) const override;
+  sk_sp<SkTypeface> onMakeFromFile(const char path[],
+                                   int ttc_index) const override;
+  sk_sp<SkTypeface> onLegacyMakeTypeface(const char family_name[],
+                                         SkFontStyle) const override;
+
+ private:
+  class FontCache;
+
+  fuchsia::fonts::FontProviderSync2Ptr font_provider_;
+
+  // Map applied to font family name before sending requests to the FontService.
+  base::flat_map<std::string, std::string> font_map_;
+
+  // FontCache keeps all SkTypeface instances returned by the manager. It allows
+  // to ensure that SkTypeface object is created only once for each typeface.
+  std::unique_ptr<FontCache> font_cache_;
+
+  sk_sp<SkTypeface> default_typeface_;
+
+  DISALLOW_COPY_AND_ASSIGN(FuchsiaFontManager);
+};
+
+}  // namespace skia
+
+#endif  // SKIA_EXT_FONTMGR_FUCHSIA_H_
diff --git a/skia/ext/fontmgr_fuchsia_unittest.cc b/skia/ext/fontmgr_fuchsia_unittest.cc
new file mode 100644
index 0000000..74c0216c
--- /dev/null
+++ b/skia/ext/fontmgr_fuchsia_unittest.cc
@@ -0,0 +1,163 @@
+// 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 "skia/ext/fontmgr_fuchsia.h"
+
+#include <fuchsia/fonts/cpp/fidl.h>
+#include <lib/fidl/cpp/binding.h>
+
+#include "base/base_paths.h"
+#include "base/bind.h"
+#include "base/files/file_util.h"
+#include "base/fuchsia/fuchsia_logging.h"
+#include "base/location.h"
+#include "base/message_loop/message_loop.h"
+#include "base/path_service.h"
+#include "base/task_runner.h"
+#include "base/threading/thread.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace skia {
+
+namespace {
+
+constexpr zx_rights_t kFontDataRights =
+    ZX_RIGHTS_BASIC | ZX_RIGHT_READ | ZX_RIGHT_MAP;
+
+fuchsia::fonts::FontData LoadFont(const base::FilePath& file_path) {
+  std::string file_content;
+  CHECK(ReadFileToString(file_path, &file_content));
+  fuchsia::fonts::FontData data;
+  zx_status_t status =
+      zx::vmo::create(file_content.size(), 0, &data.buffer.vmo);
+  ZX_CHECK(status == ZX_OK, status);
+  status = data.buffer.vmo.write(file_content.data(), 0, file_content.size());
+  ZX_CHECK(status == ZX_OK, status);
+  data.buffer.size = file_content.size();
+  return data;
+}
+
+class MockFontProvider : public fuchsia::fonts::FontProvider {
+ public:
+  MockFontProvider() {
+    base::FilePath assets_dir;
+    EXPECT_TRUE(base::PathService::Get(base::DIR_ASSETS, &assets_dir));
+
+    // Roboto is not in test_fonts. Just load some arbitrary fonts for the
+    // tests.
+    roboto_ = LoadFont(assets_dir.Append("test_fonts/Arimo-Regular.ttf"));
+    roboto_slab_ = LoadFont(assets_dir.Append("test_fonts/Tinos-Regular.ttf"));
+  }
+
+  // fuchsia::fonts::FontProvider implementation.
+  void GetFont(fuchsia::fonts::FontRequest request,
+               GetFontCallback callback) override {
+    fuchsia::fonts::FontData* font_data = nullptr;
+    if (*request.family == "Roboto") {
+      font_data = &roboto_;
+    } else if (*request.family == "RobotoSlab") {
+      font_data = &roboto_slab_;
+    }
+
+    if (!font_data) {
+      callback(nullptr);
+      return;
+    }
+
+    auto response = fuchsia::fonts::FontResponse::New();
+    EXPECT_EQ(font_data->buffer.vmo.duplicate(kFontDataRights,
+                                              &(response->data.buffer.vmo)),
+              ZX_OK);
+    response->data.buffer.size = font_data->buffer.size;
+    callback(std::move(response));
+  }
+
+ private:
+  fuchsia::fonts::FontData roboto_;
+  fuchsia::fonts::FontData roboto_slab_;
+};
+
+class MockFontProviderService {
+ public:
+  MockFontProviderService() : provider_thread_("MockFontProvider") {
+    provider_thread_.StartWithOptions(
+        base::Thread::Options(base::MessageLoop::TYPE_IO, 0));
+  }
+
+  ~MockFontProviderService() {
+    provider_thread_.task_runner()->DeleteSoon(FROM_HERE,
+                                               std::move(provider_binding_));
+  }
+
+  void Bind(fidl::InterfaceRequest<fuchsia::fonts::FontProvider> request) {
+    provider_thread_.task_runner()->PostTask(
+        FROM_HERE, base::BindOnce(&MockFontProviderService::DoBind,
+                                  base::Unretained(this), std::move(request)));
+  }
+
+ private:
+  void DoBind(fidl::InterfaceRequest<fuchsia::fonts::FontProvider> request) {
+    provider_binding_ =
+        std::make_unique<fidl::Binding<fuchsia::fonts::FontProvider>>(
+            &provider_, std::move(request));
+  }
+
+  base::Thread provider_thread_;
+
+  MockFontProvider provider_;
+  std::unique_ptr<fidl::Binding<fuchsia::fonts::FontProvider>>
+      provider_binding_;
+};
+
+}  // namespace
+
+class FuchsiaFontManagerTest : public testing::Test {
+ public:
+  FuchsiaFontManagerTest() {
+    fuchsia::fonts::FontProviderSync2Ptr font_provider;
+    font_provider_service_.Bind(font_provider.NewRequest());
+    font_manager_ = sk_make_sp<FuchsiaFontManager>(std::move(font_provider));
+  }
+
+ protected:
+  MockFontProviderService font_provider_service_;
+  sk_sp<SkFontMgr> font_manager_;
+};
+
+// Verify that SkTypeface objects are cached.
+TEST_F(FuchsiaFontManagerTest, Caching) {
+  sk_sp<SkTypeface> sans(
+      font_manager_->matchFamilyStyle("sans", SkFontStyle()));
+  sk_sp<SkTypeface> sans2(
+      font_manager_->matchFamilyStyle("sans", SkFontStyle()));
+
+  // Expect that the same SkTypeface is returned for both requests.
+  EXPECT_EQ(sans.get(), sans2.get());
+
+  // Request serif and verify that a different SkTypeface is returned.
+  sk_sp<SkTypeface> serif(
+      font_manager_->matchFamilyStyle("serif", SkFontStyle()));
+  EXPECT_NE(sans.get(), serif.get());
+}
+
+// Verify that SkTypeface can outlive the manager.
+TEST_F(FuchsiaFontManagerTest, TypefaceOutlivesManager) {
+  sk_sp<SkTypeface> sans(
+      font_manager_->matchFamilyStyle("sans", SkFontStyle()));
+  font_manager_.reset();
+}
+
+// Verify that we can query a font after releasing a previous instance.
+TEST_F(FuchsiaFontManagerTest, ReleaseThenCreateAgain) {
+  sk_sp<SkTypeface> serif(
+      font_manager_->matchFamilyStyle("serif", SkFontStyle()));
+  EXPECT_TRUE(serif);
+  serif.reset();
+
+  sk_sp<SkTypeface> serif2(
+      font_manager_->matchFamilyStyle("serif", SkFontStyle()));
+  EXPECT_TRUE(serif2);
+}
+
+}  // namespace skia
\ No newline at end of file
diff --git a/testing/buildbot/filters/mojo.fyi.network_browser_tests.filter b/testing/buildbot/filters/mojo.fyi.network_browser_tests.filter
index b75fb54..deb1267 100644
--- a/testing/buildbot/filters/mojo.fyi.network_browser_tests.filter
+++ b/testing/buildbot/filters/mojo.fyi.network_browser_tests.filter
@@ -265,6 +265,8 @@
 -ExtensionWebRequestApiTest.WebRequestTestOSDD
 # Note WebRequestUnloadImmediately is disabled on Linux
 -ExtensionWebRequestApiTest.WebRequestUnloadImmediately
+# http://crbug.com/853118 Flaky with serivicified network stack.
+-LocalNTPInterceptionWebRequestAPITest.OneGoogleBarRequestsHidden
 
 # WebRequest implementation needs to handle extension
 # installation/uninstallation events.
diff --git a/third_party/WebKit/LayoutTests/FlagExpectations/enable-blink-features=LayoutNG b/third_party/WebKit/LayoutTests/FlagExpectations/enable-blink-features=LayoutNG
index 910cfca..0297261d 100644
--- a/third_party/WebKit/LayoutTests/FlagExpectations/enable-blink-features=LayoutNG
+++ b/third_party/WebKit/LayoutTests/FlagExpectations/enable-blink-features=LayoutNG
@@ -17,9 +17,6 @@
 crbug.com/854889 accessibility/css-generated-content.html [ Failure ]
 crbug.com/854889 accessibility/css-styles.html [ Failure ]
 
-# Wrong image alignment in vertical-rl writing mode
-crbug.com/636993 fast/writing-mode/vertical-baseline-alignment.html [ Failure ]
-
 # Wrong quirks mode line height for pattern <div><a><img></a></div>
 crbug.com/854840 fast/table/backgr_border-table-quirks-collapsed-border.html [ Failure ]
 crbug.com/854840 fast/table/backgr_border-table-quirks.html [ Failure ]
@@ -297,7 +294,6 @@
 crbug.com/591099 external/wpt/html-media-capture/capture_video_cancel-manual.html [ Failure ]
 crbug.com/591099 external/wpt/html/browsers/sandboxing/sandbox-disallow-same-origin.html [ Pass ]
 crbug.com/591099 external/wpt/html/dom/documents/dom-tree-accessors/Document.currentScript.html [ Pass ]
-crbug.com/591099 external/wpt/html/rendering/non-replaced-elements/the-fieldset-element-0/legend-block-formatting-context.html [ Failure ]
 crbug.com/591099 external/wpt/html/rendering/replaced-elements/svg-embedded-sizing/svg-in-img-auto.html [ Failure ]
 crbug.com/591099 external/wpt/html/rendering/replaced-elements/svg-embedded-sizing/svg-in-img-percentage.html [ Failure ]
 crbug.com/591099 external/wpt/html/rendering/replaced-elements/svg-inline-sizing/svg-inline.html [ Failure ]
@@ -453,7 +449,6 @@
 crbug.com/591099 fast/lists/001-vertical.html [ Failure ]
 crbug.com/591099 fast/lists/003-vertical.html [ Failure ]
 crbug.com/591099 fast/lists/004.html [ Failure ]
-crbug.com/591099 fast/lists/009-vertical.html [ Failure ]
 crbug.com/591099 fast/lists/list-item-line-height.html [ Failure ]
 crbug.com/591099 fast/lists/marker-before-empty-inline.html [ Failure ]
 crbug.com/591099 fast/masking/clip-path-selection.html [ Failure ]
@@ -535,7 +530,6 @@
 crbug.com/591099 fast/writing-mode/text-combine-various-fonts.html [ Failure ]
 crbug.com/591099 fast/writing-mode/vertical-rl-replaced-selection.html [ Failure ]
 crbug.com/591099 fullscreen/full-screen-with-flex-item.html [ Failure ]
-crbug.com/591099 hittesting/border-hittest-inlineFlowBox.html [ Failure ]
 crbug.com/591099 hittesting/inline-with-clip-path.html [ Failure ]
 crbug.com/856730 html/details_summary/details-mouse-click.html [ Failure Pass ]
 crbug.com/855039 html/details_summary/details-writing-mode-align-center.html [ Failure ]
@@ -724,6 +718,8 @@
 crbug.com/591099 paint/invalidation/svg/scale-change-huge-geometry.html [ Failure ]
 crbug.com/591099 paint/invalidation/svg/scroll-hit-test.xhtml [ Failure ]
 crbug.com/728378 paint/invalidation/svg/scrolling-embedded-svg-file-image-repaint-problem.html [ Failure ]
+# rightsizing-grid.html is truly flaky, show flakiness on reload
+crbug.com/591099 svg/wicd/rightsizing-grid.html [ Pass Failure ]
 crbug.com/591099 paint/invalidation/table/cached-change-cell-sl-border-color.html [ Failure ]
 crbug.com/591099 paint/invalidation/table/collapsed-border-cell-resize.html [ Failure ]
 crbug.com/591099 paint/invalidation/table/composited-table-row.html [ Failure ]
diff --git a/third_party/WebKit/LayoutTests/FlagExpectations/site-per-process b/third_party/WebKit/LayoutTests/FlagExpectations/site-per-process
index c3997d392..4c210e0 100644
--- a/third_party/WebKit/LayoutTests/FlagExpectations/site-per-process
+++ b/third_party/WebKit/LayoutTests/FlagExpectations/site-per-process
@@ -161,7 +161,7 @@
 crbug.com/834185 http/tests/fetch/serviceworker-proxied/thorough/redirect-nocors-base-https-other-https.html [ Pass Timeout ]
 crbug.com/834185 http/tests/fetch/serviceworker-proxied/thorough/redirect-password-base-https-other-https.html [ Pass Timeout ]
 crbug.com/834185 http/tests/fetch/serviceworker-proxied/thorough/scheme-blob-base-https-other-https.html [ Pass Timeout ]
-crbug.com/834185 http/tests/fetch/serviceworker-proxied/thorough/scheme-data-base-https-other-https.html [ Pass Timeout ]
+crbug.com/859988 http/tests/fetch/serviceworker-proxied/thorough/scheme-data-base-https-other-https.html [ Failure Pass Timeout ]
 crbug.com/834185 http/tests/fetch/serviceworker/thorough/access-control-base-https-other-https.html [ Pass Timeout ]
 crbug.com/834185 http/tests/fetch/serviceworker/thorough/auth-base-https-other-https.html [ Pass Timeout ]
 crbug.com/834185 http/tests/fetch/serviceworker/thorough/auth-nocors-base-https-other-https.html [ Pass Timeout ]
diff --git a/third_party/WebKit/LayoutTests/NeverFixTests b/third_party/WebKit/LayoutTests/NeverFixTests
index 213048c..efcc065 100644
--- a/third_party/WebKit/LayoutTests/NeverFixTests
+++ b/third_party/WebKit/LayoutTests/NeverFixTests
@@ -88,9 +88,6 @@
 [ Mac ] virtual/user-activation-v2/fast/events/menu-key-context-menu-reveal-focus.html [ WontFix ]
 [ Mac ] virtual/user-activation-v2/fast/events/menu-key-context-menu.html [ WontFix ]
 
-# Link Highlighting is only supported on Windows and Linux
-[ Mac ] compositing/gestures [ WontFix ]
-
 # These tests are specific to Linux.
 [ Mac Win ] editing/input/linux_ltr_composition_underline.html [ WontFix ]
 [ Mac Win ] editing/input/linux_rtl_composition_underline.html [ WontFix ]
diff --git a/third_party/WebKit/LayoutTests/TestExpectations b/third_party/WebKit/LayoutTests/TestExpectations
index b25c522..7d088537 100644
--- a/third_party/WebKit/LayoutTests/TestExpectations
+++ b/third_party/WebKit/LayoutTests/TestExpectations
@@ -297,8 +297,6 @@
 crbug.com/704961 [ Mac ] virtual/layout_ng/external/wpt/css/CSS2/normal-flow/width-inherit-001.xht [ Failure ]
 
 # Inline: border in continuations. Fail in Blink.
-crbug.com/635619 virtual/layout_ng/external/wpt/css/CSS2/normal-flow/block-in-inline-empty-001.xht [ Failure ]
-crbug.com/635619 virtual/layout_ng/external/wpt/css/CSS2/normal-flow/block-in-inline-empty-004.xht [ Failure ]
 crbug.com/635619 virtual/layout_ng/external/wpt/css/CSS2/normal-flow/block-in-inline-insert-001f.xht [ Failure ]
 crbug.com/635619 virtual/layout_ng/external/wpt/css/CSS2/normal-flow/block-in-inline-insert-002f.xht [ Failure ]
 
@@ -435,7 +433,6 @@
 crbug.com/714962 [ Mac ] virtual/layout_ng/fast/block/float/trailing-float-with-content.html [ Failure ]
 
 ### virtual/layout_ng/fast/writing-mode/
-crbug.com/714962 virtual/layout_ng/fast/writing-mode/Kusa-Makura-background-canvas.html [ Failure ]
 crbug.com/714962 virtual/layout_ng/fast/writing-mode/auto-sizing-orthogonal-flows.html [ Failure ]
 crbug.com/714962 virtual/layout_ng/fast/writing-mode/background-vertical-lr.html [ Failure ]
 crbug.com/714962 virtual/layout_ng/fast/writing-mode/background-vertical-rl.html [ Failure ]
@@ -454,16 +451,10 @@
 crbug.com/714962 virtual/layout_ng/fast/writing-mode/japanese-lr-selection.html [ Failure ]
 crbug.com/714962 [ Mac Win ] virtual/layout_ng/fast/writing-mode/japanese-lr-text.html [ Failure ]
 crbug.com/714962 virtual/layout_ng/fast/writing-mode/japanese-rl-selection.html [ Failure ]
-crbug.com/714962 virtual/layout_ng/fast/writing-mode/japanese-ruby-vertical-lr.html [ Failure ]
-crbug.com/714962 virtual/layout_ng/fast/writing-mode/japanese-ruby-vertical-rl.html [ Failure ]
 crbug.com/714962 [ Mac ] virtual/layout_ng/fast/writing-mode/margins.html [ Failure ]
 crbug.com/714962 [ Mac ] virtual/layout_ng/fast/writing-mode/orthogonal-inline-block.html [ Failure ]
 crbug.com/714962 virtual/layout_ng/fast/writing-mode/orthogonal-writing-modes-available-width-absolute-crash.html [ Failure ]
 crbug.com/714962 virtual/layout_ng/fast/writing-mode/percentage-height-orthogonal-writing-modes.html [ Failure ]
-crbug.com/714962 virtual/layout_ng/fast/writing-mode/percentage-margins-absolute-replaced.html [ Failure ]
-crbug.com/714962 virtual/layout_ng/fast/writing-mode/percentage-margins-absolute.html [ Failure ]
-crbug.com/714962 virtual/layout_ng/fast/writing-mode/table-percent-width-quirk.html [ Failure ]
-crbug.com/714962 virtual/layout_ng/fast/writing-mode/text-combine-compress.html [ Failure ]
 crbug.com/714962 virtual/layout_ng/fast/writing-mode/text-combine-justify.html [ Failure ]
 crbug.com/714962 virtual/layout_ng/fast/writing-mode/text-combine-line-break.html [ Failure ]
 crbug.com/714962 virtual/layout_ng/fast/writing-mode/text-combine-various-fonts.html [ Failure ]
@@ -752,14 +743,12 @@
 crbug.com/591099 virtual/layout_ng_experimental/fast/multicol/dynamic/spanner-before-content-becomes-regular-block.html [ Failure ]
 crbug.com/591099 virtual/layout_ng_experimental/fast/multicol/dynamic/spanner-in-content-becomes-regular-block.html [ Failure ]
 crbug.com/591099 virtual/layout_ng_experimental/fast/multicol/dynamic/static-becomes-relpos-has-abspos.html [ Failure ]
-crbug.com/714962 virtual/layout_ng_experimental/fast/multicol/event-offset-complex-tree.html [ Failure ]
 crbug.com/591099 virtual/layout_ng_experimental/fast/multicol/event-offset-in-nested.html [ Failure ]
 crbug.com/591099 virtual/layout_ng_experimental/fast/multicol/first-line-in-float-below-next-column-top.html [ Failure ]
 crbug.com/591099 virtual/layout_ng_experimental/fast/multicol/first-line-in-float-with-margin.html [ Failure ]
 crbug.com/591099 virtual/layout_ng_experimental/fast/multicol/fixedpos-child-becomes-static.html [ Failure ]
 crbug.com/591099 virtual/layout_ng_experimental/fast/multicol/flexbox-starts-at-column-boundary-with-block.html [ Failure ]
 crbug.com/591099 virtual/layout_ng_experimental/fast/multicol/flexbox-starts-at-column-boundary.html [ Failure ]
-crbug.com/591099 virtual/layout_ng_experimental/fast/multicol/flexbox-with-overflow-auto-child-crash.html [ Timeout ]
 crbug.com/591099 virtual/layout_ng_experimental/fast/multicol/flexbox.html [ Failure ]
 crbug.com/591099 virtual/layout_ng_experimental/fast/multicol/flipped-blocks-border-after.html [ Failure ]
 crbug.com/591099 virtual/layout_ng_experimental/fast/multicol/flipped-blocks-hit-test.html [ Failure ]
@@ -771,6 +760,7 @@
 crbug.com/591099 virtual/layout_ng_experimental/fast/multicol/float-edge.html [ Failure ]
 crbug.com/591099 virtual/layout_ng_experimental/fast/multicol/float-margin-at-row-boundary-fixed-multicol-height.html [ Failure ]
 crbug.com/591099 virtual/layout_ng_experimental/fast/multicol/float-margin-at-row-boundary.html [ Failure ]
+crbug.com/591099 [ Mac ] virtual/layout_ng_experimental/fast/multicol/flexbox-with-overflow-auto-child-crash.html [ Timeout ]
 crbug.com/591099 virtual/layout_ng_experimental/fast/multicol/float-moved-by-child-line-and-unbreakable.html [ Failure ]
 crbug.com/591099 virtual/layout_ng_experimental/fast/multicol/float-paginate-empty-lines.html [ Failure ]
 crbug.com/591099 virtual/layout_ng_experimental/fast/multicol/float-paginate.html [ Failure ]
@@ -1327,8 +1317,6 @@
 crbug.com/591099 virtual/layout_ng_experimental/css3/flexbox/mozilla/flexbox-sizing-vert-1.xhtml [ Skip ]
 
 ### virtual/layout_ng_experimental/external/wpt/css/css-flexbox/
-crbug.com/591099 virtual/layout_ng_experimental/external/wpt/css/css-flexbox/item-with-table-with-infinite-max-intrinsic-width.html [ Failure ]
-crbug.com/591099 virtual/layout_ng_experimental/external/wpt/css/css-flexbox/table-with-infinite-max-intrinsic-width.html [ Failure ]
 crbug.com/591099 virtual/layout_ng_experimental/external/wpt/css/css-flexbox/Flexible-order.html [ Failure ]
 crbug.com/591099 virtual/layout_ng_experimental/external/wpt/css/css-flexbox/abspos-autopos-htb-ltr.html [ Skip ]
 crbug.com/591099 virtual/layout_ng_experimental/external/wpt/css/css-flexbox/abspos-autopos-htb-rtl.html [ Skip ]
@@ -1607,9 +1595,9 @@
 crbug.com/591099 virtual/layout_ng_experimental/external/wpt/css/css-flexbox/flexbox_rtl-flow-reverse.html [ Skip ]
 crbug.com/591099 virtual/layout_ng_experimental/external/wpt/css/css-flexbox/flexbox_rtl-flow.html [ Skip ]
 crbug.com/591099 virtual/layout_ng_experimental/external/wpt/css/css-flexbox/flexbox_rtl-order.html [ Skip ]
+crbug.com/857185 [ Mac ] virtual/layout_ng_experimental/external/wpt/css/css-flexbox/flexbox_stf-table-caption.html [ Crash ]
 crbug.com/591099 virtual/layout_ng_experimental/external/wpt/css/css-flexbox/flexbox_stf-table-singleline-2.html [ Failure ]
 crbug.com/591099 virtual/layout_ng_experimental/external/wpt/css/css-flexbox/flexbox_stf-table-singleline.html [ Failure ]
-crbug.com/857185 virtual/layout_ng_experimental/external/wpt/css/css-flexbox/flexbox_stf-table-caption.html [ Crash ]
 crbug.com/336604 virtual/layout_ng_experimental/external/wpt/css/css-flexbox/flexbox_visibility-collapse-line-wrapping.html [ Failure ]
 crbug.com/336604 virtual/layout_ng_experimental/external/wpt/css/css-flexbox/flexbox_visibility-collapse.html [ Failure ]
 crbug.com/591099 virtual/layout_ng_experimental/external/wpt/css/css-flexbox/flexbox_wrap-long.html [ Failure ]
@@ -3207,7 +3195,6 @@
 crbug.com/626703 [ Mac10.11 Retina ] external/wpt/html/semantics/embedded-content/media-elements/track/track-element/track-remove-active-cue.html [ Crash ]
 crbug.com/626703 [ Linux Win ] external/wpt/css/css-ui/text-overflow-028.html [ Failure ]
 crbug.com/626703 external/wpt/html/dom/elements/global-attributes/title-manual.html [ Skip ]
-crbug.com/626703 external/wpt/resource-timing/resource_timing.worker.html [ Timeout ]
 crbug.com/626703 external/wpt/css/css-text/astral-bidi/adlam-anti-ref.html [ Pass Failure ]
 crbug.com/626703 external/wpt/css/css-text/astral-bidi/cypriot-anti-ref.html [ Pass Failure ]
 crbug.com/626703 external/wpt/css/css-fill-stroke/paint-order-001.tentative.html [ Failure ]
@@ -4676,8 +4663,6 @@
 
 # Sheriff 2018-06-11
 crbug.com/850202 [ Linux ] http/tests/devtools/network/network-filters.js [ Pass Failure ]
-crbug.com/851746 [ Linux ] fast/canvas/color-space/canvas-colorManaged-convertToBlob-roundtrip.html [ Pass Timeout ]
-crbug.com/851746 [ Linux ] virtual/gpu/fast/canvas/color-space/canvas-colorManaged-convertToBlob-roundtrip.html [ Pass Timeout ]
 
 crbug.com/853360 [ Mac ] fast/css/input-search-padding.html [ Failure ]
 crbug.com/853360 [ Mac ] fast/forms/calendar-picker/calendar-picker-appearance-ar.html [ Failure ]
@@ -4744,3 +4729,10 @@
 
 # Sheriff 2018-07-02
 crbug.com/859605 [ Mac10.10 ] external/wpt/shadow-dom/untriaged/events/retargeting-focus-events/test-002.html [ Failure ]
+crbug.com/859681 [ Mac10.13 ] http/tests/devtools/tracing/timeline-time/timeline-time.js [ Failure ]
+crbug.com/859681 [ Mac10.13 ] http/tests/devtools/tracing/timeline-time/timeline-usertiming.js [ Failure ]
+crbug.com/859681 [ Mac10.13 ] virtual/threaded/http/tests/devtools/tracing/console-timeline.js [ Failure ]
+crbug.com/859681 [ Linux Mac10.13 ] virtual/threaded/http/tests/devtools/tracing/timeline-time/timeline-time.js [ Failure ]
+
+# Sheriff 2018-07-03
+crbug.com/860031 [ Linux ] http/tests/object/remote-frame-crash.html [ Failure ]
diff --git a/third_party/WebKit/LayoutTests/external/WPT_BASE_MANIFEST.json b/third_party/WebKit/LayoutTests/external/WPT_BASE_MANIFEST.json
index 52b922ef..1bbad14 100644
--- a/third_party/WebKit/LayoutTests/external/WPT_BASE_MANIFEST.json
+++ b/third_party/WebKit/LayoutTests/external/WPT_BASE_MANIFEST.json
@@ -158032,6 +158032,11 @@
      {}
     ]
    ],
+   "interfaces/picture-in-picture.idl": [
+    [
+     {}
+    ]
+   ],
    "interfaces/pointerevents-extension.idl": [
     [
      {}
@@ -160417,6 +160422,11 @@
      {}
     ]
    ],
+   "picture-in-picture/idlharness.window-expected.txt": [
+    [
+     {}
+    ]
+   ],
    "picture-in-picture/resources/picture-in-picture-helpers.js": [
     [
      {}
@@ -236954,6 +236964,12 @@
      }
     ]
    ],
+   "picture-in-picture/idlharness.window.js": [
+    [
+     "/picture-in-picture/idlharness.window.html",
+     {}
+    ]
+   ],
    "picture-in-picture/leave-picture-in-picture.html": [
     [
      "/picture-in-picture/leave-picture-in-picture.html",
@@ -375915,6 +375931,10 @@
    "7fec46d25cf175390524b681cdbec7b0b76c89b9",
    "support"
   ],
+  "interfaces/picture-in-picture.idl": [
+   "3e97112286c64d274a4ecbdb0f41d4700e5448fb",
+   "support"
+  ],
   "interfaces/pointerevents-extension.idl": [
    "26055736a27852501eba7da7f0cab7c57eceb652",
    "support"
@@ -386615,6 +386635,14 @@
    "4bcd6f2c68c4f6b9b17057d57d6cfe6101845e9d",
    "testharness"
   ],
+  "picture-in-picture/idlharness.window-expected.txt": [
+   "4968c95baaefdba77792785cf39a1b47fc1dac9c",
+   "support"
+  ],
+  "picture-in-picture/idlharness.window.js": [
+   "4b24a93a59e290c762969a4f8c33e24ebe37bf9b",
+   "testharness"
+  ],
   "picture-in-picture/leave-picture-in-picture.html": [
    "9f19a0c59604198b67a687b14455be6e5aad7fa8",
    "testharness"
@@ -397452,7 +397480,7 @@
    "support"
   ],
   "service-workers/cache-storage/script-tests/cache-storage-match.js": [
-   "e3cc14ea5d0587c43a5b142fca84788383b549cb",
+   "946b95407abe9b6fabef438da5fc9152fa6721f0",
    "support"
   ],
   "service-workers/cache-storage/script-tests/cache-storage.js": [
diff --git a/third_party/WebKit/LayoutTests/external/wpt/fetch/api/response/response-cancel-stream.html b/third_party/WebKit/LayoutTests/external/wpt/fetch/api/response/response-cancel-stream.html
index 58d6745..fcaed04 100644
--- a/third_party/WebKit/LayoutTests/external/wpt/fetch/api/response/response-cancel-stream.html
+++ b/third_party/WebKit/LayoutTests/external/wpt/fetch/api/response/response-cancel-stream.html
@@ -30,7 +30,9 @@
     var closedPromise = reader.closed.then(function() {
         return reader.cancel();
     });
-    reader.read();
+    reader.read().then(function readMore({done, value}) {
+      if (!done) return reader.read().then(readMore);
+    });
     return closedPromise;
 }, "Cancelling a closed blob Response stream");
 
diff --git a/third_party/WebKit/LayoutTests/external/wpt/interfaces/picture-in-picture.idl b/third_party/WebKit/LayoutTests/external/wpt/interfaces/picture-in-picture.idl
new file mode 100644
index 0000000..e15515d0
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/interfaces/picture-in-picture.idl
@@ -0,0 +1,31 @@
+// GENERATED CONTENT - DO NOT EDIT
+// Content of this file was automatically extracted from the
+// "Picture-in-Picture" spec.
+// See: https://wicg.github.io/picture-in-picture/
+
+partial interface HTMLVideoElement {
+  Promise<PictureInPictureWindow> requestPictureInPicture();
+
+  attribute EventHandler onenterpictureinpicture;
+  attribute EventHandler onleavepictureinpicture;
+
+  [CEReactions]
+  attribute boolean disablePictureInPicture;
+};
+
+partial interface Document {
+  readonly attribute boolean pictureInPictureEnabled;
+
+  Promise<void> exitPictureInPicture();
+};
+
+partial interface DocumentOrShadowRoot {
+  readonly attribute Element? pictureInPictureElement;
+};
+
+interface PictureInPictureWindow {
+  readonly attribute long width;
+  readonly attribute long height;
+
+  attribute EventHandler onresize;
+};
diff --git a/third_party/WebKit/LayoutTests/external/wpt/picture-in-picture/idlharness.window-expected.txt b/third_party/WebKit/LayoutTests/external/wpt/picture-in-picture/idlharness.window-expected.txt
new file mode 100644
index 0000000..d94d9007
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/picture-in-picture/idlharness.window-expected.txt
@@ -0,0 +1,36 @@
+This is a testharness.js-based test.
+PASS picture-in-picture interfaces.
+PASS Partial interface HTMLVideoElement: original interface defined
+PASS Partial interface Document: original interface defined
+PASS Partial interface DocumentOrShadowRoot: original interface defined
+FAIL PictureInPictureWindow interface: existence and properties of interface object assert_equals: prototype of self's property "PictureInPictureWindow" is not Function.prototype expected function "function () { [native code] }" but got function "function EventTarget() { [native code] }"
+PASS PictureInPictureWindow interface object length
+PASS PictureInPictureWindow interface object name
+FAIL PictureInPictureWindow interface: existence and properties of interface prototype object assert_equals: prototype of PictureInPictureWindow.prototype is not Object.prototype expected object "[object Object]" but got object "[object EventTarget]"
+PASS PictureInPictureWindow interface: existence and properties of interface prototype object's "constructor" property
+PASS PictureInPictureWindow interface: existence and properties of interface prototype object's @@unscopables property
+PASS PictureInPictureWindow interface: attribute width
+PASS Unscopable handled correctly for width property on PictureInPictureWindow
+PASS PictureInPictureWindow interface: attribute height
+PASS Unscopable handled correctly for height property on PictureInPictureWindow
+PASS PictureInPictureWindow interface: attribute onresize
+PASS Unscopable handled correctly for onresize property on PictureInPictureWindow
+PASS Document interface: attribute pictureInPictureEnabled
+PASS Unscopable handled correctly for pictureInPictureEnabled property on Document
+PASS Document interface: operation exitPictureInPicture()
+PASS Unscopable handled correctly for exitPictureInPicture() on Document
+PASS Document interface: attribute pictureInPictureElement
+PASS Unscopable handled correctly for pictureInPictureElement property on Document
+PASS Document interface: document must inherit property "pictureInPictureEnabled" with the proper type
+PASS Document interface: document must inherit property "exitPictureInPicture()" with the proper type
+PASS Document interface: document must inherit property "pictureInPictureElement" with the proper type
+PASS HTMLVideoElement interface: operation requestPictureInPicture()
+PASS Unscopable handled correctly for requestPictureInPicture() on HTMLVideoElement
+PASS HTMLVideoElement interface: attribute onenterpictureinpicture
+PASS Unscopable handled correctly for onenterpictureinpicture property on HTMLVideoElement
+PASS HTMLVideoElement interface: attribute onleavepictureinpicture
+PASS Unscopable handled correctly for onleavepictureinpicture property on HTMLVideoElement
+PASS HTMLVideoElement interface: attribute disablePictureInPicture
+PASS Unscopable handled correctly for disablePictureInPicture property on HTMLVideoElement
+Harness: the test ran to completion.
+
diff --git a/third_party/WebKit/LayoutTests/external/wpt/picture-in-picture/idlharness.window.js b/third_party/WebKit/LayoutTests/external/wpt/picture-in-picture/idlharness.window.js
new file mode 100644
index 0000000..d86c676
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/picture-in-picture/idlharness.window.js
@@ -0,0 +1,22 @@
+// META: script=/resources/WebIDLParser.js
+// META: script=/resources/idlharness.js
+
+'use strict';
+
+// https://wicg.github.io/picture-in-picture/
+
+promise_test(async () => {
+  const srcs = ['html', 'dom', 'picture-in-picture'];
+  const [html, dom, pip] = await Promise.all(
+      srcs.map(i => fetch(`/interfaces/${i}.idl`).then(r => r.text())));
+
+  const idl_array = new IdlArray();
+  idl_array.add_idls(pip);
+  idl_array.add_dependency_idls(dom);
+  idl_array.add_dependency_idls(html);
+
+  idl_array.add_objects({
+    Document: ['document'],
+  });
+  idl_array.test();
+}, 'picture-in-picture interfaces.');
diff --git a/third_party/WebKit/LayoutTests/fast/canvas/color-space/canvas-colorManaged-convertToBlob-roundtrip.html b/third_party/WebKit/LayoutTests/fast/canvas/color-space/canvas-colorManaged-convertToBlob-roundtrip.html
index 4ad118de..c0665c3b 100644
--- a/third_party/WebKit/LayoutTests/fast/canvas/color-space/canvas-colorManaged-convertToBlob-roundtrip.html
+++ b/third_party/WebKit/LayoutTests/fast/canvas/color-space/canvas-colorManaged-convertToBlob-roundtrip.html
@@ -148,7 +148,9 @@
         {colorSpace: 'srgb', pixelFormat: 'float16'},
         {colorSpace: 'rec2020', pixelFormat: 'float16'},
         {colorSpace: 'p3', pixelFormat: 'float16'}];
-    var alphaValues = [0.5, 1];
+    // crbug.com/859102
+//    var alphaValues = [0.5, 1];
+    var alphaValues = [1];
 
     // The *correct* way to test convertToBlob() is to directly examine the
     // image file for the expected color space and pixels. Since this is not
diff --git a/third_party/WebKit/LayoutTests/fast/canvas/color-space/canvas-createImageBitmap-linear-rgb.html b/third_party/WebKit/LayoutTests/fast/canvas/color-space/canvas-createImageBitmap-linear-rgb.html
index 11da83a..7a06cfac 100644
--- a/third_party/WebKit/LayoutTests/fast/canvas/color-space/canvas-createImageBitmap-linear-rgb.html
+++ b/third_party/WebKit/LayoutTests/fast/canvas/color-space/canvas-createImageBitmap-linear-rgb.html
@@ -42,6 +42,9 @@
         actual = ctx.getImageData(tests[i][0], tests[i][1], 1, 1).dataUnion;
         expected = tests[i][2];
         assert_true(actual.length === expected.length);
+        // crbug.com/859102
+        if (expected[3] > 0 && expected[3] < 1)
+            continue;
        for (var j = 0; j < actual.length; j++)
            assert_approx_equals(actual[j], expected[j], tolerance, tests[i][3]);
     }
diff --git a/third_party/WebKit/LayoutTests/fast/canvas/color-space/canvas-createImageBitmap-p3.html b/third_party/WebKit/LayoutTests/fast/canvas/color-space/canvas-createImageBitmap-p3.html
index dab3b0a..45ca3da 100644
--- a/third_party/WebKit/LayoutTests/fast/canvas/color-space/canvas-createImageBitmap-p3.html
+++ b/third_party/WebKit/LayoutTests/fast/canvas/color-space/canvas-createImageBitmap-p3.html
@@ -38,6 +38,9 @@
         actual = ctx.getImageData(tests[i][0], tests[i][1], 1, 1).dataUnion;
         expected = tests[i][2];
         assert_true(actual.length === expected.length);
+        // crbug.com/859102
+        if (expected[3] > 0 && expected[3] < 1)
+            continue;
        for (var j = 0; j < actual.length; j++)
            assert_approx_equals(actual[j], expected[j], tolerance, tests[i][3]);
     }
diff --git a/third_party/WebKit/LayoutTests/fast/canvas/color-space/canvas-createImageBitmap-rec2020.html b/third_party/WebKit/LayoutTests/fast/canvas/color-space/canvas-createImageBitmap-rec2020.html
index c666da48..66cf1dd 100644
--- a/third_party/WebKit/LayoutTests/fast/canvas/color-space/canvas-createImageBitmap-rec2020.html
+++ b/third_party/WebKit/LayoutTests/fast/canvas/color-space/canvas-createImageBitmap-rec2020.html
@@ -42,6 +42,9 @@
         actual = ctx.getImageData(tests[i][0], tests[i][1], 1, 1).dataUnion;
         expected = tests[i][2];
         assert_true(actual.length === expected.length);
+        // crbug.com/859102
+        if (expected[3] > 0 && expected[3] < 1)
+            continue;
        for (var j = 0; j < actual.length; j++)
            assert_approx_equals(actual[j], expected[j], tolerance, tests[i][3]);
     }
diff --git a/third_party/WebKit/LayoutTests/fast/canvas/color-space/transferFromImageBitmap.html b/third_party/WebKit/LayoutTests/fast/canvas/color-space/transferFromImageBitmap.html
index 706691e7..5ccb5e16 100644
--- a/third_party/WebKit/LayoutTests/fast/canvas/color-space/transferFromImageBitmap.html
+++ b/third_party/WebKit/LayoutTests/fast/canvas/color-space/transferFromImageBitmap.html
@@ -108,7 +108,9 @@
         {colorSpace: 'rec2020', pixelFormat: 'float16'},
         {colorSpace: 'p3', pixelFormat: 'float16'}
         ];
-    var alphaValues = [0.5, 1];
+    // crbug.com/859102
+//    var alphaValues = [0.5, 1];
+    var alphaValues = [1];
     var colorSpaceConversions = [
         'none', 'default', 'srgb', 'linear-rgb', 'rec2020', 'p3'];
 
diff --git a/third_party/WebKit/LayoutTests/fast/dom/Document/document-open-use-counters-0args.html b/third_party/WebKit/LayoutTests/fast/dom/Document/document-open-use-counters-0args.html
new file mode 100644
index 0000000..d8cafd3
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/fast/dom/Document/document-open-use-counters-0args.html
@@ -0,0 +1,27 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>document.open UseCounters with no arguments</title>
+<script src="../../../resources/testharness.js"></script>
+<script src="../../../resources/testharnessreport.js"></script>
+<body>
+<script>
+const DocumentOpenTwoArgs = 2494; // From public/platform/web_feature.mojom
+const DocumentOpenTwoArgsWithReplace = 2495; // From public/platform/web_feature.mojom
+const DocumentOpenThreeArgs = 2496; // From public/platform/web_feature.mojom
+
+test(() => {
+  const frame = document.body.appendChild(document.createElement('iframe'));
+  try {
+    assert_false(internals.isUseCounted(frame.contentDocument, DocumentOpenTwoArgs), "DocumentOpenTwoArgs: before");
+    assert_false(internals.isUseCounted(frame.contentDocument, DocumentOpenTwoArgsWithReplace), "DocumentOpenTwoArgsWithReplace: before");
+    assert_false(internals.isUseCounted(frame.contentDocument, DocumentOpenThreeArgs), "DocumentOpenThreeArgs: before");
+    frame.contentDocument.open();
+    assert_true(internals.isUseCounted(frame.contentDocument, DocumentOpenTwoArgs), "DocumentOpenTwoArgs: after");
+    assert_false(internals.isUseCounted(frame.contentDocument, DocumentOpenTwoArgsWithReplace), "DocumentOpenTwoArgsWithReplace: after");
+    assert_false(internals.isUseCounted(frame.contentDocument, DocumentOpenThreeArgs), "DocumentOpenThreeArgs: after");
+  } finally {
+    frame.remove();
+  }
+}, 'document.open() with zero arguments');
+</script>
+</body>
diff --git a/third_party/WebKit/LayoutTests/fast/dom/Document/document-open-use-counters-1arg.html b/third_party/WebKit/LayoutTests/fast/dom/Document/document-open-use-counters-1arg.html
new file mode 100644
index 0000000..785f3bee
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/fast/dom/Document/document-open-use-counters-1arg.html
@@ -0,0 +1,27 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>document.open UseCounters with one argument</title>
+<script src="../../../resources/testharness.js"></script>
+<script src="../../../resources/testharnessreport.js"></script>
+<body>
+<script>
+const DocumentOpenTwoArgs = 2494; // From public/platform/web_feature.mojom
+const DocumentOpenTwoArgsWithReplace = 2495; // From public/platform/web_feature.mojom
+const DocumentOpenThreeArgs = 2496; // From public/platform/web_feature.mojom
+
+test(() => {
+  const frame = document.body.appendChild(document.createElement('iframe'));
+  try {
+    assert_false(internals.isUseCounted(frame.contentDocument, DocumentOpenTwoArgs), "DocumentOpenTwoArgs: before");
+    assert_false(internals.isUseCounted(frame.contentDocument, DocumentOpenTwoArgsWithReplace), "DocumentOpenTwoArgsWithReplace: before");
+    assert_false(internals.isUseCounted(frame.contentDocument, DocumentOpenThreeArgs), "DocumentOpenThreeArgs: before");
+    frame.contentDocument.open('text/html');
+    assert_true(internals.isUseCounted(frame.contentDocument, DocumentOpenTwoArgs), "DocumentOpenTwoArgs: after");
+    assert_false(internals.isUseCounted(frame.contentDocument, DocumentOpenTwoArgsWithReplace), "DocumentOpenTwoArgsWithReplace: after");
+    assert_false(internals.isUseCounted(frame.contentDocument, DocumentOpenThreeArgs), "DocumentOpenThreeArgs: after");
+  } finally {
+    frame.remove();
+  }
+}, 'document.open() with one argument');
+</script>
+</body>
diff --git a/third_party/WebKit/LayoutTests/fast/dom/Document/document-open-use-counters-2args-replace.html b/third_party/WebKit/LayoutTests/fast/dom/Document/document-open-use-counters-2args-replace.html
new file mode 100644
index 0000000..6718555
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/fast/dom/Document/document-open-use-counters-2args-replace.html
@@ -0,0 +1,27 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>document.open UseCounters with two arguments and replace</title>
+<script src="../../../resources/testharness.js"></script>
+<script src="../../../resources/testharnessreport.js"></script>
+<body>
+<script>
+const DocumentOpenTwoArgs = 2494; // From public/platform/web_feature.mojom
+const DocumentOpenTwoArgsWithReplace = 2495; // From public/platform/web_feature.mojom
+const DocumentOpenThreeArgs = 2496; // From public/platform/web_feature.mojom
+
+test(() => {
+  const frame = document.body.appendChild(document.createElement('iframe'));
+  try {
+    assert_false(internals.isUseCounted(frame.contentDocument, DocumentOpenTwoArgs), "DocumentOpenTwoArgs: before");
+    assert_false(internals.isUseCounted(frame.contentDocument, DocumentOpenTwoArgsWithReplace), "DocumentOpenTwoArgsWithReplace: before");
+    assert_false(internals.isUseCounted(frame.contentDocument, DocumentOpenThreeArgs), "DocumentOpenThreeArgs: before");
+    frame.contentDocument.open('text/html', 'replace');
+    assert_true(internals.isUseCounted(frame.contentDocument, DocumentOpenTwoArgs), "DocumentOpenTwoArgs: after");
+    assert_true(internals.isUseCounted(frame.contentDocument, DocumentOpenTwoArgsWithReplace), "DocumentOpenTwoArgsWithReplace: after");
+    assert_false(internals.isUseCounted(frame.contentDocument, DocumentOpenThreeArgs), "DocumentOpenThreeArgs: after");
+  } finally {
+    frame.remove();
+  }
+}, 'document.open() with two arguments and replace');
+</script>
+</body>
diff --git a/third_party/WebKit/LayoutTests/fast/dom/Document/document-open-use-counters-2args.html b/third_party/WebKit/LayoutTests/fast/dom/Document/document-open-use-counters-2args.html
new file mode 100644
index 0000000..8b51891
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/fast/dom/Document/document-open-use-counters-2args.html
@@ -0,0 +1,27 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>document.open UseCounters with two arguments but no replace</title>
+<script src="../../../resources/testharness.js"></script>
+<script src="../../../resources/testharnessreport.js"></script>
+<body>
+<script>
+const DocumentOpenTwoArgs = 2494; // From public/platform/web_feature.mojom
+const DocumentOpenTwoArgsWithReplace = 2495; // From public/platform/web_feature.mojom
+const DocumentOpenThreeArgs = 2496; // From public/platform/web_feature.mojom
+
+test(() => {
+  const frame = document.body.appendChild(document.createElement('iframe'));
+  try {
+    assert_false(internals.isUseCounted(frame.contentDocument, DocumentOpenTwoArgs), "DocumentOpenTwoArgs: before");
+    assert_false(internals.isUseCounted(frame.contentDocument, DocumentOpenTwoArgsWithReplace), "DocumentOpenTwoArgsWithReplace: before");
+    assert_false(internals.isUseCounted(frame.contentDocument, DocumentOpenThreeArgs), "DocumentOpenThreeArgs: before");
+    frame.contentDocument.open('text/html', '');
+    assert_true(internals.isUseCounted(frame.contentDocument, DocumentOpenTwoArgs), "DocumentOpenTwoArgs: after");
+    assert_false(internals.isUseCounted(frame.contentDocument, DocumentOpenTwoArgsWithReplace), "DocumentOpenTwoArgsWithReplace: after");
+    assert_false(internals.isUseCounted(frame.contentDocument, DocumentOpenThreeArgs), "DocumentOpenThreeArgs: after");
+  } finally {
+    frame.remove();
+  }
+}, 'document.open() with two arguments but no replace');
+</script>
+</body>
diff --git a/third_party/WebKit/LayoutTests/fast/dom/Document/document-open-use-counters-3args.html b/third_party/WebKit/LayoutTests/fast/dom/Document/document-open-use-counters-3args.html
new file mode 100644
index 0000000..b3256b2
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/fast/dom/Document/document-open-use-counters-3args.html
@@ -0,0 +1,27 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>document.open UseCounters with three arguments</title>
+<script src="../../../resources/testharness.js"></script>
+<script src="../../../resources/testharnessreport.js"></script>
+<body>
+<script>
+const DocumentOpenTwoArgs = 2494; // From public/platform/web_feature.mojom
+const DocumentOpenTwoArgsWithReplace = 2495; // From public/platform/web_feature.mojom
+const DocumentOpenThreeArgs = 2496; // From public/platform/web_feature.mojom
+
+test(() => {
+  const frame = document.body.appendChild(document.createElement('iframe'));
+  try {
+    assert_false(internals.isUseCounted(frame.contentDocument, DocumentOpenTwoArgs), "DocumentOpenTwoArgs: before");
+    assert_false(internals.isUseCounted(frame.contentDocument, DocumentOpenTwoArgsWithReplace), "DocumentOpenTwoArgsWithReplace: before");
+    assert_false(internals.isUseCounted(frame.contentDocument, DocumentOpenThreeArgs), "DocumentOpenThreeArgs: before");
+    frame.contentDocument.open('about:blank', '_blank', '');
+    assert_false(internals.isUseCounted(frame.contentDocument, DocumentOpenTwoArgs), "DocumentOpenTwoArgs: after");
+    assert_false(internals.isUseCounted(frame.contentDocument, DocumentOpenTwoArgsWithReplace), "DocumentOpenTwoArgsWithReplace: after");
+    assert_true(internals.isUseCounted(frame.contentDocument, DocumentOpenThreeArgs), "DocumentOpenThreeArgs: after");
+  } finally {
+    frame.remove();
+  }
+}, 'document.open() with three arguments');
+</script>
+</body>
diff --git a/third_party/WebKit/LayoutTests/fast/scroll-snap/animate-fling-to-snap-points.html b/third_party/WebKit/LayoutTests/fast/scroll-snap/animate-fling-to-snap-points.html
deleted file mode 100644
index dbbe8ce..0000000
--- a/third_party/WebKit/LayoutTests/fast/scroll-snap/animate-fling-to-snap-points.html
+++ /dev/null
@@ -1,62 +0,0 @@
-<!DOCTYPE html>
-<script src="../../resources/testharness.js"></script>
-<script src="../../resources/testharnessreport.js"></script>
-<script src="../../resources/gesture-util.js"></script>
-<style>
-div {
-  position: absolute;
-}
-#scroller {
-  width: 500px;
-  height: 500px;
-  overflow: scroll;
-  scroll-snap-type: both mandatory;
-  border: solid black 5px;
-}
-#space {
-  width: 2000px;
-  height: 2000px;
-}
-.target {
-  width: 200px;
-  height: 200px;
-  scroll-snap-align: start;
-  background-color: blue;
-}
-</style>
-
-<div id="scroller">
-  <div id="space"></div>
-  <div class="target" style="left: 0px; top: 0px;"></div>
-  <div class="target" style="left: 80px; top: 80px;"></div>
-  <div class="target" style="left: 800px; top: 800px;"></div>
-</div>
-
-<script>
-var scroller = document.getElementById("scroller");
-
-function scrollLeft() {
-  return scroller.scrollLeft;
-}
-
-promise_test (async () => {
-  scroller.scrollTo(0, 0);
-  await smoothScroll(100, 200, 200, GestureSourceType.TOUCH_INPUT, 'downright', SPEED_INSTANT);
-  await waitForAnimationEnd(scrollLeft, 700, 10);
-  await waitFor( () => {
-    return approx_equals(scroller.scrollLeft, 80, 1) &&
-           approx_equals(scroller.scrollTop, 80, 1);
-  });
-}, "Without fling enabled, the scroll ends at the closest snap point to the scroll destination.");
-
-promise_test (async () => {
-  scroller.scrollTo(0, 0);
-  await swipe(100, 200, 200, 'upleft', 3000);
-  await waitForAnimationEnd(scrollLeft, 700, 15);
-  await waitFor( () => {
-    return approx_equals(scroller.scrollLeft, 800, 1) &&
-           approx_equals(scroller.scrollTop, 800, 1);
-  });
-}, "With fling enabled, the scroll ends at the closest snap point to the fling destination.")
-
-</script>
\ No newline at end of file
diff --git a/third_party/WebKit/LayoutTests/http/tests/devtools/persistence/persistence-sync-content-nodejs.js b/third_party/WebKit/LayoutTests/http/tests/devtools/persistence/persistence-sync-content-nodejs.js
index 1c69db7..0235580 100644
--- a/third_party/WebKit/LayoutTests/http/tests/devtools/persistence/persistence-sync-content-nodejs.js
+++ b/third_party/WebKit/LayoutTests/http/tests/devtools/persistence/persistence-sync-content-nodejs.js
@@ -9,7 +9,7 @@
 
   var testMapping = BindingsTestRunner.initializeTestMapping();
   // Pretend we are running under V8 front-end.
-  SDK.targetManager.mainTarget().setIsNodeJSForTest();
+  SDK.targetManager.mainTarget().markAsNodeJS();
 
   var content = ['', '', 'var express = require("express");', '//TODO'].join('\n');
 
diff --git a/third_party/WebKit/LayoutTests/http/tests/devtools/sources/debugger-breakpoints/nodejs-set-breakpoint.js b/third_party/WebKit/LayoutTests/http/tests/devtools/sources/debugger-breakpoints/nodejs-set-breakpoint.js
index e1fb786..fcbb511 100644
--- a/third_party/WebKit/LayoutTests/http/tests/devtools/sources/debugger-breakpoints/nodejs-set-breakpoint.js
+++ b/third_party/WebKit/LayoutTests/http/tests/devtools/sources/debugger-breakpoints/nodejs-set-breakpoint.js
@@ -8,7 +8,7 @@
   await TestRunner.loadModule('sdk_test_runner');
   await TestRunner.showPanel('sources');
 
-  SDK.targetManager.mainTarget().setIsNodeJSForTest();
+  SDK.targetManager.mainTarget().markAsNodeJS();
   SourcesTestRunner.startDebuggerTest();
 
   var debuggerModel = SDK.targetManager.mainTarget().model(SDK.DebuggerModel);
diff --git a/third_party/WebKit/LayoutTests/platform/linux/editing/selection/modify_extend/extend_by_character-expected.txt b/third_party/WebKit/LayoutTests/platform/linux/editing/selection/modify_extend/extend_by_character-expected.txt
deleted file mode 100644
index 41b8f82..0000000
--- a/third_party/WebKit/LayoutTests/platform/linux/editing/selection/modify_extend/extend_by_character-expected.txt
+++ /dev/null
@@ -1,9 +0,0 @@
-This is a testharness.js-based test.
-PASS extend forward character on characters
-PASS extend forward character through image
-PASS extend forward character through multiple spaces
-PASS extend backward character through image
-PASS extend backward character through multiple spaces
-PASS extend forward character over element
-Harness: the test ran to completion.
-
diff --git a/third_party/WebKit/LayoutTests/platform/mac/compositing/gestures/gesture-tapHighlight-pixel-rotated-div-expected.png b/third_party/WebKit/LayoutTests/platform/mac/compositing/gestures/gesture-tapHighlight-pixel-rotated-div-expected.png
new file mode 100644
index 0000000..714b5dbc
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/platform/mac/compositing/gestures/gesture-tapHighlight-pixel-rotated-div-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/mac/compositing/gestures/gesture-tapHighlight-pixel-rotated-link-expected.png b/third_party/WebKit/LayoutTests/platform/mac/compositing/gestures/gesture-tapHighlight-pixel-rotated-link-expected.png
new file mode 100644
index 0000000..e2f96fc
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/platform/mac/compositing/gestures/gesture-tapHighlight-pixel-rotated-link-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/mac/compositing/gestures/gesture-tapHighlight-pixel-transparent-expected.png b/third_party/WebKit/LayoutTests/platform/mac/compositing/gestures/gesture-tapHighlight-pixel-transparent-expected.png
new file mode 100644
index 0000000..e8374ce
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/platform/mac/compositing/gestures/gesture-tapHighlight-pixel-transparent-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/mac/compositing/gestures/gesture-tapHighlight-skew-matrix-expected.png b/third_party/WebKit/LayoutTests/platform/mac/compositing/gestures/gesture-tapHighlight-skew-matrix-expected.png
new file mode 100644
index 0000000..8a9e6c7
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/platform/mac/compositing/gestures/gesture-tapHighlight-skew-matrix-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/mac/compositing/gestures/gesture-tapHighlight-with-box-shadow-expected.png b/third_party/WebKit/LayoutTests/platform/mac/compositing/gestures/gesture-tapHighlight-with-box-shadow-expected.png
new file mode 100644
index 0000000..e254387
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/platform/mac/compositing/gestures/gesture-tapHighlight-with-box-shadow-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/mac/compositing/gestures/gesture-tapHighlight-with-squashing-expected.png b/third_party/WebKit/LayoutTests/platform/mac/compositing/gestures/gesture-tapHighlight-with-squashing-expected.png
new file mode 100644
index 0000000..0abc887
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/platform/mac/compositing/gestures/gesture-tapHighlight-with-squashing-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/win/editing/selection/modify_extend/extend_by_character-expected.txt b/third_party/WebKit/LayoutTests/platform/win/editing/selection/modify_extend/extend_by_character-expected.txt
deleted file mode 100644
index ced8a3f2..0000000
--- a/third_party/WebKit/LayoutTests/platform/win/editing/selection/modify_extend/extend_by_character-expected.txt
+++ /dev/null
@@ -1,15 +0,0 @@
-This is a testharness.js-based test.
-PASS extend forward character on characters
-PASS extend forward character through image
-FAIL extend forward character through multiple spaces LayoutTests/editing/selection/modify_extend/extend_by_character.html:42:12)
-	 expected <div contenteditable> <i>F    ^and seven</i> years <b>  as </b>our fathers  f upon this continent, a new nation, conceived    in Liberty,   and dedicated to the proposition that all  <br>men are created equal|. </div>,
-	 but got  <div contenteditable> <i>F    ^and seven</i> years <b>  as </b>our fathers  f upon this continent, a new nation, conceived    in Liberty,   and dedicated to the proposition that all  <br>men are created equ|al. </div>,
-	 sameupto <div contenteditable> <i>F    ^and seven</i> years <b>  as </b>our fathers  f upon this continent, a new nation, conceived    in Liberty,   and dedicated to the proposition that all  <br>men are created equ
-PASS extend backward character through image
-FAIL extend backward character through multiple spaces LayoutTests/editing/selection/modify_extend/extend_by_character.html:92:12)
-	 expected <div contenteditable> <i>|F    and seven</i> years <b>  as </b>our fathers  f upon this continent, a new nation, conceived    in Liberty,   and dedicated to the proposition that all  <br>men are created equ^al. </div>,
-	 but got  <div contenteditable> <i>F    |and seven</i> years <b>  as </b>our fathers  f upon this continent, a new nation, conceived    in Liberty,   and dedicated to the proposition that all  <br>men are created equ^al. </div>,
-	 sameupto <div contenteditable> <i>
-PASS extend forward character over element
-Harness: the test ran to completion.
-
diff --git a/third_party/WebKit/LayoutTests/resources/gesture-util.js b/third_party/WebKit/LayoutTests/resources/gesture-util.js
index 393cbef..84fbde0d 100644
--- a/third_party/WebKit/LayoutTests/resources/gesture-util.js
+++ b/third_party/WebKit/LayoutTests/resources/gesture-util.js
@@ -27,30 +27,6 @@
   });
 }
 
-function waitForAnimationEnd(getValue, max_frame, max_unchanged_frame) {
-  const MAX_FRAME = max_frame;
-  const MAX_UNCHANGED_FRAME = max_unchanged_frame;
-  var last_changed_frame = 0;
-  var last_position = getValue();
-  return new Promise((resolve, reject) => {
-    function tick(frames) {
-    // We requestAnimationFrame either for MAX_FRAME or until
-    // MAX_UNCHANGED_FRAME with no change have been observed.
-      if (frames >= MAX_FRAME || frames - last_changed_frame > MAX_UNCHANGED_FRAME) {
-        resolve();
-      } else {
-        current_value = getValue();
-        if (last_position != current_value) {
-          last_changed_frame = frames;
-          last_position = current_value;
-        }
-        requestAnimationFrame(tick.bind(this, frames + 1));
-      }
-    }
-    tick(0);
-  })
-}
-
 // Enums for gesture_source_type parameters in gpuBenchmarking synthetic
 // gesture methods. Must match C++ side enums in synthetic_gesture_params.h
 const GestureSourceType = {
@@ -84,21 +60,6 @@
   });
 }
 
-function swipe(pixels_to_scroll, start_x, start_y, direction, speed_in_pixels_s) {
-  return new Promise((resolve, reject) => {
-    if (chrome && chrome.gpuBenchmarking) {
-      chrome.gpuBenchmarking.swipe(direction,
-                                   pixels_to_scroll,
-                                   resolve,
-                                   start_x,
-                                   start_y,
-                                   speed_in_pixels_s);
-    } else {
-      reject('This test requires chrome.gpuBenchmarking');
-    }
-  })
-}
-
 function pinchBy(scale, centerX, centerY, speed_in_pixels_s, gesture_source_type) {
   return new Promise((resolve, reject) => {
     if (chrome && chrome.gpuBenchmarking) {
@@ -196,7 +157,3 @@
     }
   });
 }
-
-function approx_equals(actual, expected, epsilon) {
-  return actual >= expected - epsilon && actual <= expected + epsilon;
-}
\ No newline at end of file
diff --git a/third_party/android_async_task/BUILD.gn b/third_party/android_async_task/BUILD.gn
deleted file mode 100644
index d385fb7c..0000000
--- a/third_party/android_async_task/BUILD.gn
+++ /dev/null
@@ -1,16 +0,0 @@
-# Copyright 2016 The Chromium Authors. All rights reserved.
-# Use of this source code is governed by a BSD-style license that can be
-# found in the LICENSE file.
-
-import("//build/config/android/rules.gni")
-
-assert(is_android)
-
-android_library("android_async_task_java") {
-  java_files = [ "java/src/org/chromium/third_party/android/os/AsyncTask.java" ]
-  deps = [
-    "//base:base_java",
-    "//third_party/android_tools:android_support_annotations_java",
-  ]
-  chromium_code = false
-}
diff --git a/third_party/android_async_task/README.chromium b/third_party/android_async_task/README.chromium
index a30a0c99..b5a734f 100644
--- a/third_party/android_async_task/README.chromium
+++ b/third_party/android_async_task/README.chromium
@@ -1,14 +1,17 @@
 Name: AsyncTask
-URL: https://chromium.googlesource.com/android_tools.git/+/master/sdk/sources/android-23/android/os/AsyncTask.java
-Version: f4c36ad89b2696b37d9cd7ca7d984b691888b188
+URL: https://android.googlesource.com/platform/frameworks/base/+/oreo-release/core/java/android/os/AsyncTask.java
+Version: 1488a3a19d4681a41fb45570c15e14d99db1cb66
 License: Apache 2.0
 License File: LICENSE
 Security Critical: yes
 
 Description:
-This contains a copy of Android sdk 23's AsyncTask.java.
+This contains a copy of Android Oreo's AsyncTask.java.
 
 Local Modifications:
-- Renamed package to org.chromium.third_party.android.os.
+- Renamed package to org.chromium.base.
 - Explicitly import android.os objects.
 - Switch to using android.support.annotations (from android.support.v13).
+- Added annotations to help it compile issue-free.
+- Changed THREAD_POOL_EXECUTOR to reuse the threads from android.os.AsyncTask
+- Changed SERIAL_EXECUTOR to reuse the executor from android.os.AsyncTask
diff --git a/third_party/android_async_task/java/src/org/chromium/third_party/android/os/AsyncTask.java b/third_party/android_async_task/java/src/org/chromium/base/AsyncTask.java
similarity index 87%
rename from third_party/android_async_task/java/src/org/chromium/third_party/android/os/AsyncTask.java
rename to third_party/android_async_task/java/src/org/chromium/base/AsyncTask.java
index 2b3669e..a1b91ed 100644
--- a/third_party/android_async_task/java/src/org/chromium/third_party/android/os/AsyncTask.java
+++ b/third_party/android_async_task/java/src/org/chromium/base/AsyncTask.java
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package org.chromium.third_party.android.os;
+package org.chromium.base;
 
 import android.os.Binder;
 import android.os.Handler;
@@ -22,14 +22,15 @@
 import android.os.Message;
 import android.os.Process;
 import android.support.annotation.MainThread;
+import android.support.annotation.Nullable;
 import android.support.annotation.WorkerThread;
 
 import java.util.ArrayDeque;
 import java.util.concurrent.BlockingQueue;
 import java.util.concurrent.Callable;
 import java.util.concurrent.CancellationException;
-import java.util.concurrent.Executor;
 import java.util.concurrent.ExecutionException;
+import java.util.concurrent.Executor;
 import java.util.concurrent.FutureTask;
 import java.util.concurrent.LinkedBlockingQueue;
 import java.util.concurrent.ThreadFactory;
@@ -40,8 +41,8 @@
 import java.util.concurrent.atomic.AtomicInteger;
 
 /**
- * <p>AsyncTask enables proper and easy use of the UI thread. This class allows to
- * perform background operations and publish results on the UI thread without
+ * <p>AsyncTask enables proper and easy use of the UI thread. This class allows you
+ * to perform background operations and publish results on the UI thread without
  * having to manipulate threads and/or handlers.</p>
  *
  * <p>AsyncTask is designed to be a helper class around {@link Thread} and {@link Handler}
@@ -60,7 +61,7 @@
  * <div class="special reference">
  * <h3>Developer Guides</h3>
  * <p>For more information about using tasks and threads, read the
- * <a href="{@docRoot}guide/topics/fundamentals/processes-and-threads.html">Processes and
+ * <a href="{@docRoot}guide/components/processes-and-threads.html">Processes and
  * Threads</a> developer guide.</p>
  * </div>
  *
@@ -138,7 +139,7 @@
  *     computation finishes. The result of the background computation is passed to
  *     this step as a parameter.</li>
  * </ol>
- * 
+ *
  * <h2>Cancelling a task</h2>
  * <p>A task can be cancelled at any time by invoking {@link #cancel(boolean)}. Invoking
  * this method will cause subsequent calls to {@link #isCancelled()} to return true.
@@ -186,13 +187,17 @@
     private static final String LOG_TAG = "AsyncTask";
 
     private static final int CPU_COUNT = Runtime.getRuntime().availableProcessors();
-    private static final int CORE_POOL_SIZE = CPU_COUNT + 1;
+    // We want at least 2 threads and at most 4 threads in the core pool,
+    // preferring to have 1 less than the CPU count to avoid saturating
+    // the CPU with background work
+    private static final int CORE_POOL_SIZE = Math.max(2, Math.min(CPU_COUNT - 1, 4));
     private static final int MAXIMUM_POOL_SIZE = CPU_COUNT * 2 + 1;
-    private static final int KEEP_ALIVE = 1;
+    private static final int KEEP_ALIVE_SECONDS = 30;
 
     private static final ThreadFactory sThreadFactory = new ThreadFactory() {
         private final AtomicInteger mCount = new AtomicInteger(1);
 
+        @Override
         public Thread newThread(Runnable r) {
             return new Thread(r, "AsyncTask #" + mCount.getAndIncrement());
         }
@@ -204,15 +209,13 @@
     /**
      * An {@link Executor} that can be used to execute tasks in parallel.
      */
-    public static final Executor THREAD_POOL_EXECUTOR
-            = new ThreadPoolExecutor(CORE_POOL_SIZE, MAXIMUM_POOL_SIZE, KEEP_ALIVE,
-                    TimeUnit.SECONDS, sPoolWorkQueue, sThreadFactory);
+    public static final Executor THREAD_POOL_EXECUTOR = android.os.AsyncTask.THREAD_POOL_EXECUTOR;
 
     /**
      * An {@link Executor} that executes tasks one at a time in serial
      * order.  This serialization is global to a particular process.
      */
-    public static final Executor SERIAL_EXECUTOR = new SerialExecutor();
+    public static final Executor SERIAL_EXECUTOR = android.os.AsyncTask.SERIAL_EXECUTOR;
 
     private static final int MESSAGE_POST_RESULT = 0x1;
     private static final int MESSAGE_POST_PROGRESS = 0x2;
@@ -224,16 +227,20 @@
     private final FutureTask<Result> mFuture;
 
     private volatile Status mStatus = Status.PENDING;
-    
+
     private final AtomicBoolean mCancelled = new AtomicBoolean();
     private final AtomicBoolean mTaskInvoked = new AtomicBoolean();
 
+    private final Handler mHandler;
+
     private static class SerialExecutor implements Executor {
         final ArrayDeque<Runnable> mTasks = new ArrayDeque<Runnable>();
         Runnable mActive;
 
+        @Override
         public synchronized void execute(final Runnable r) {
             mTasks.offer(new Runnable() {
+                @Override
                 public void run() {
                     try {
                         r.run();
@@ -273,15 +280,19 @@
         FINISHED,
     }
 
-    private static Handler getHandler() {
+    private static Handler getMainHandler() {
         synchronized (AsyncTask.class) {
             if (sHandler == null) {
-                sHandler = new InternalHandler();
+                sHandler = new InternalHandler(Looper.getMainLooper());
             }
             return sHandler;
         }
     }
 
+    private Handler getHandler() {
+        return mHandler;
+    }
+
     /** @hide */
     public static void setDefaultExecutor(Executor exec) {
         sDefaultExecutor = exec;
@@ -291,15 +302,45 @@
      * Creates a new asynchronous task. This constructor must be invoked on the UI thread.
      */
     public AsyncTask() {
+        this((Looper) null);
+    }
+
+    /**
+     * Creates a new asynchronous task. This constructor must be invoked on the UI thread.
+     *
+     * @hide
+     */
+    public AsyncTask(@Nullable Handler handler) {
+        this(handler != null ? handler.getLooper() : null);
+    }
+
+    /**
+     * Creates a new asynchronous task. This constructor must be invoked on the UI thread.
+     *
+     * @hide
+     */
+    public AsyncTask(@Nullable Looper callbackLooper) {
+        mHandler = callbackLooper == null || callbackLooper == Looper.getMainLooper()
+                ? getMainHandler()
+                : new Handler(callbackLooper);
+
         mWorker = new WorkerRunnable<Params, Result>() {
+            @Override
             public Result call() throws Exception {
                 mTaskInvoked.set(true);
-
-                Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
-                //noinspection unchecked
-                Result result = doInBackground(mParams);
-                Binder.flushPendingCommands();
-                return postResult(result);
+                Result result = null;
+                try {
+                    Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
+                    // noinspection unchecked
+                    result = doInBackground(mParams);
+                    Binder.flushPendingCommands();
+                } catch (Throwable tr) {
+                    mCancelled.set(true);
+                    throw tr;
+                } finally {
+                    postResult(result);
+                }
+                return result;
             }
         };
 
@@ -311,8 +352,8 @@
                 } catch (InterruptedException e) {
                     android.util.Log.w(LOG_TAG, e);
                 } catch (ExecutionException e) {
-                    throw new RuntimeException("An error occurred while executing doInBackground()",
-                            e.getCause());
+                    throw new RuntimeException(
+                            "An error occurred while executing doInBackground()", e.getCause());
                 } catch (CancellationException e) {
                     postResultIfNotInvoked(null);
                 }
@@ -329,8 +370,8 @@
 
     private Result postResult(Result result) {
         @SuppressWarnings("unchecked")
-        Message message = getHandler().obtainMessage(MESSAGE_POST_RESULT,
-                new AsyncTaskResult<Result>(this, result));
+        Message message = getHandler().obtainMessage(
+                MESSAGE_POST_RESULT, new AsyncTaskResult<Result>(this, result));
         message.sendToTarget();
         return result;
     }
@@ -360,7 +401,7 @@
      * @see #onPostExecute
      * @see #publishProgress
      */
-    @SuppressWarnings("unchecked")
+    @SuppressWarnings({"unchecked"})
     @WorkerThread
     protected abstract Result doInBackground(Params... params);
 
@@ -371,25 +412,23 @@
      * @see #doInBackground
      */
     @MainThread
-    protected void onPreExecute() {
-    }
+    protected void onPreExecute() {}
 
     /**
      * <p>Runs on the UI thread after {@link #doInBackground}. The
      * specified result is the value returned by {@link #doInBackground}.</p>
-     * 
+     *
      * <p>This method won't be invoked if the task was cancelled.</p>
      *
      * @param result The result of the operation computed by {@link #doInBackground}.
      *
      * @see #onPreExecute
      * @see #doInBackground
-     * @see #onCancelled(Object) 
+     * @see #onCancelled(Object)
      */
     @SuppressWarnings({"UnusedDeclaration"})
     @MainThread
-    protected void onPostExecute(Result result) {
-    }
+    protected void onPostExecute(Result result) {}
 
     /**
      * Runs on the UI thread after {@link #publishProgress} is invoked.
@@ -402,20 +441,19 @@
      */
     @SuppressWarnings({"UnusedDeclaration", "unchecked"})
     @MainThread
-    protected void onProgressUpdate(Progress... values) {
-    }
+    protected void onProgressUpdate(Progress... values) {}
 
     /**
      * <p>Runs on the UI thread after {@link #cancel(boolean)} is invoked and
      * {@link #doInBackground(Object[])} has finished.</p>
-     * 
+     *
      * <p>The default implementation simply invokes {@link #onCancelled()} and
      * ignores the result. If you write your own implementation, do not call
      * <code>super.onCancelled(result)</code>.</p>
      *
      * @param result The result, if any, computed in
      *               {@link #doInBackground(Object[])}, can be null
-     * 
+     *
      * @see #cancel(boolean)
      * @see #isCancelled()
      */
@@ -423,23 +461,22 @@
     @MainThread
     protected void onCancelled(Result result) {
         onCancelled();
-    }    
-    
+    }
+
     /**
      * <p>Applications should preferably override {@link #onCancelled(Object)}.
      * This method is invoked by the default implementation of
      * {@link #onCancelled(Object)}.</p>
-     * 
+     *
      * <p>Runs on the UI thread after {@link #cancel(boolean)} is invoked and
      * {@link #doInBackground(Object[])} has finished.</p>
      *
-     * @see #onCancelled(Object) 
+     * @see #onCancelled(Object)
      * @see #cancel(boolean)
      * @see #isCancelled()
      */
     @MainThread
-    protected void onCancelled() {
-    }
+    protected void onCancelled() {}
 
     /**
      * Returns <tt>true</tt> if this task was cancelled before it completed
@@ -464,7 +501,7 @@
      * then the <tt>mayInterruptIfRunning</tt> parameter determines
      * whether the thread executing this task should be interrupted in
      * an attempt to stop the task.</p>
-     * 
+     *
      * <p>Calling this method will result in {@link #onCancelled(Object)} being
      * invoked on the UI thread after {@link #doInBackground(Object[])}
      * returns. Calling this method guarantees that {@link #onPostExecute(Object)}
@@ -519,15 +556,15 @@
      *         while waiting.
      * @throws TimeoutException If the wait timed out.
      */
-    public final Result get(long timeout, TimeUnit unit) throws InterruptedException,
-            ExecutionException, TimeoutException {
+    public final Result get(long timeout, TimeUnit unit)
+            throws InterruptedException, ExecutionException, TimeoutException {
         return mFuture.get(timeout, unit);
     }
 
     /**
      * Executes the task with the specified parameters. The task returns
      * itself (this) so that the caller can keep a reference to it.
-     * 
+     *
      * <p>Note: this function schedules the task on a queue for a single background
      * thread or pool of threads depending on the platform version.  When first
      * introduced, AsyncTasks were executed serially on a single background thread.
@@ -552,7 +589,7 @@
      * @see #executeOnExecutor(java.util.concurrent.Executor, Object[])
      * @see #execute(Runnable)
      */
-    @SuppressWarnings("unchecked")
+    @SuppressWarnings({"unchecked"})
     @MainThread
     public final AsyncTask<Params, Progress, Result> execute(Params... params) {
         return executeOnExecutor(sDefaultExecutor, params);
@@ -561,12 +598,12 @@
     /**
      * Executes the task with the specified parameters. The task returns
      * itself (this) so that the caller can keep a reference to it.
-     * 
+     *
      * <p>This method is typically used with {@link #THREAD_POOL_EXECUTOR} to
      * allow multiple tasks to run in parallel on a pool of threads managed by
      * AsyncTask, however you can also use your own {@link Executor} for custom
      * behavior.
-     * 
+     *
      * <p><em>Warning:</em> Allowing multiple tasks to run in parallel from
      * a thread pool is generally <em>not</em> what one wants, because the order
      * of their operation is not defined.  For example, if these tasks are used
@@ -591,10 +628,10 @@
      *
      * @see #execute(Object[])
      */
-    @SuppressWarnings("unchecked")
+    @SuppressWarnings({"unchecked", "MissingCasesInEnumSwitch"})
     @MainThread
-    public final AsyncTask<Params, Progress, Result> executeOnExecutor(Executor exec,
-            Params... params) {
+    public final AsyncTask<Params, Progress, Result> executeOnExecutor(
+            Executor exec, Params... params) {
         if (mStatus != Status.PENDING) {
             switch (mStatus) {
                 case RUNNING:
@@ -644,12 +681,14 @@
      * @see #onProgressUpdate
      * @see #doInBackground
      */
-    @SuppressWarnings("unchecked")
+    @SuppressWarnings({"unchecked"})
     @WorkerThread
     protected final void publishProgress(Progress... values) {
         if (!isCancelled()) {
-            getHandler().obtainMessage(MESSAGE_POST_PROGRESS,
-                    new AsyncTaskResult<Progress>(this, values)).sendToTarget();
+            getHandler()
+                    .obtainMessage(
+                            MESSAGE_POST_PROGRESS, new AsyncTaskResult<Progress>(this, values))
+                    .sendToTarget();
         }
     }
 
@@ -663,8 +702,8 @@
     }
 
     private static class InternalHandler extends Handler {
-        public InternalHandler() {
-            super(Looper.getMainLooper());
+        public InternalHandler(Looper looper) {
+            super(looper);
         }
 
         @SuppressWarnings({"unchecked", "RawUseOfParameterizedType"})
@@ -687,7 +726,7 @@
         Params[] mParams;
     }
 
-    @SuppressWarnings({"RawUseOfParameterizedType", "unchecked"})
+    @SuppressWarnings({"unchecked", "RawUseOfParameterizedType"})
     private static class AsyncTaskResult<Data> {
         final AsyncTask mTask;
         final Data[] mData;
diff --git a/third_party/blink/public/platform/web_feature.mojom b/third_party/blink/public/platform/web_feature.mojom
index 8dde7c9..44e91a0 100644
--- a/third_party/blink/public/platform/web_feature.mojom
+++ b/third_party/blink/public/platform/web_feature.mojom
@@ -966,7 +966,6 @@
   kAddEventListenerPassiveTrue = 1417,
   kAddEventListenerPassiveFalse = 1418,
   kCSPReferrerDirective = 1419,
-  kDocumentOpen = 1420,
   kElementRequestPointerLockInShadow = 1421,
   kShadowRootPointerLockElement = 1422,
   kDocumentPointerLockElementInV0Shadow = 1423,
@@ -1954,6 +1953,9 @@
   kCSSEnvironmentVariable_SafeAreaInsetBottom = 2491,
   kCSSEnvironmentVariable_SafeAreaInsetRight = 2492,
   kMediaControlsDisplayCutoutGesture = 2493,
+  kDocumentOpenTwoArgs = 2494,
+  kDocumentOpenTwoArgsWithReplace = 2495,
+  kDocumentOpenThreeArgs = 2496,
 
   // Add new features immediately above this line. Don't change assigned
   // numbers of any item, and don't reuse removed slots.
diff --git a/third_party/blink/public/platform/web_input_event.h b/third_party/blink/public/platform/web_input_event.h
index 7b26692..c34d26dc 100644
--- a/third_party/blink/public/platform/web_input_event.h
+++ b/third_party/blink/public/platform/web_input_event.h
@@ -346,17 +346,6 @@
     return type_ == other.type_;
   }
 
-  bool IsGestureScroll() const {
-    switch (type_) {
-      case Type::kGestureScrollBegin:
-      case Type::kGestureScrollUpdate:
-      case Type::kGestureScrollEnd:
-        return true;
-      default:
-        return false;
-    }
-  }
-
   // Returns true if the WebInputEvent |type| is a pinch gesture event.
   static bool IsPinchGestureEventType(WebInputEvent::Type type) {
     return kGesturePinchTypeFirst <= type && type <= kGesturePinchTypeLast;
diff --git a/third_party/blink/renderer/core/dom/document.cc b/third_party/blink/renderer/core/dom/document.cc
index 29eb221..cc895507 100644
--- a/third_party/blink/renderer/core/dom/document.cc
+++ b/third_party/blink/renderer/core/dom/document.cc
@@ -3205,6 +3205,10 @@
                          const AtomicString& type,
                          const AtomicString& replace,
                          ExceptionState& exception_state) {
+  if (replace == "replace") {
+    UseCounter::Count(entered_window->document(),
+                      WebFeature::kDocumentOpenTwoArgsWithReplace);
+  }
   open(entered_window->document(), exception_state);
   return this;
 }
@@ -4212,12 +4216,15 @@
   // page.  Furthermore, mousemove events before the first layout should not
   // lead to a premature layout() happening, which could show a flash of white.
   // See also the similar code in EventHandler::hitTestResultAtPoint.
-  if (!GetLayoutView() || !View() || !View()->DidFirstLayout())
-    return MouseEventWithHitTestResults(event,
-                                        HitTestResult(request, LayoutPoint()));
+  if (!GetLayoutView() || !View() || !View()->DidFirstLayout()) {
+    HitTestLocation location((LayoutPoint()));
+    return MouseEventWithHitTestResults(event, location,
+                                        HitTestResult(request, location));
+  }
 
-  HitTestResult result(request, document_point);
-  GetLayoutView()->HitTest(result);
+  HitTestLocation location(document_point);
+  HitTestResult result(request, location);
+  GetLayoutView()->HitTest(location, result);
 
   if (!request.ReadOnly())
     UpdateHoverActiveState(request, result.InnerElement());
@@ -4232,7 +4239,7 @@
     result.SetCanvasRegionId(hit_test_canvas_result->GetId());
   }
 
-  return MouseEventWithHitTestResults(event, result);
+  return MouseEventWithHitTestResults(event, location, result);
 }
 
 // DOM Section 1.1.1
diff --git a/third_party/blink/renderer/core/dom/document.idl b/third_party/blink/renderer/core/dom/document.idl
index 3af7387..8159885 100644
--- a/third_party/blink/renderer/core/dom/document.idl
+++ b/third_party/blink/renderer/core/dom/document.idl
@@ -117,8 +117,8 @@
     [ImplementedAs=currentScriptForBinding] readonly attribute HTMLOrSVGScriptElement? currentScript;
 
     // dynamic markup insertion
-    [CallWith=EnteredWindow, CEReactions, CustomElementCallbacks, RaisesException, MeasureAs=DocumentOpen] Document open(optional DOMString type = "text/html", optional DOMString replace = "");
-    [CallWith=(CurrentWindow,EnteredWindow), RaisesException, MeasureAs=DocumentOpen] Window open(USVString url, DOMString name, DOMString features);
+    [CallWith=EnteredWindow, CEReactions, CustomElementCallbacks, RaisesException, MeasureAs=DocumentOpenTwoArgs] Document open(optional DOMString type = "text/html", optional DOMString replace = "");
+    [CallWith=(CurrentWindow,EnteredWindow), RaisesException, MeasureAs=DocumentOpenThreeArgs] Window open(USVString url, DOMString name, DOMString features);
     [CEReactions, RaisesException] void close();
     [CallWith=EnteredWindow, CEReactions, CustomElementCallbacks, RaisesException] void write(DOMString... text);
     [CallWith=EnteredWindow, CEReactions, CustomElementCallbacks, RaisesException] void writeln(DOMString... text);
diff --git a/third_party/blink/renderer/core/dom/element.cc b/third_party/blink/renderer/core/dom/element.cc
index cc22f3f1..8478cfe 100644
--- a/third_party/blink/renderer/core/dom/element.cc
+++ b/third_party/blink/renderer/core/dom/element.cc
@@ -971,11 +971,9 @@
 
     FloatPoint end_point(new_left * box->Style()->EffectiveZoom(),
                          box->ScrollTop().ToFloat());
-    if (RuntimeEnabledFeatures::CSSScrollSnapPointsEnabled()) {
-      end_point = GetDocument()
-                      .GetSnapCoordinator()
-                      ->GetSnapPositionForPoint(*box, end_point, true, false)
-                      .value_or(end_point);
+    if (SnapCoordinator* coordinator = GetDocument().GetSnapCoordinator()) {
+      end_point =
+          coordinator->GetSnapPositionForPoint(*box, end_point, true, false);
     }
     box->SetScrollLeft(LayoutUnit::FromFloatRound(end_point.X()));
   }
@@ -1002,11 +1000,9 @@
 
     FloatPoint end_point(box->ScrollLeft().ToFloat(),
                          new_top * box->Style()->EffectiveZoom());
-    if (RuntimeEnabledFeatures::CSSScrollSnapPointsEnabled()) {
-      end_point = GetDocument()
-                      .GetSnapCoordinator()
-                      ->GetSnapPositionForPoint(*box, end_point, false, true)
-                      .value_or(end_point);
+    if (SnapCoordinator* coordinator = GetDocument().GetSnapCoordinator()) {
+      end_point =
+          coordinator->GetSnapPositionForPoint(*box, end_point, false, true);
     }
     box->SetScrollTop(LayoutUnit::FromFloatRound(end_point.Y()));
   }
@@ -1123,14 +1119,10 @@
         top * box->Style()->EffectiveZoom() + current_scaled_top;
 
     FloatPoint new_scaled_position(new_scaled_left, new_scaled_top);
-    if (RuntimeEnabledFeatures::CSSScrollSnapPointsEnabled()) {
-      new_scaled_position =
-          GetDocument()
-              .GetSnapCoordinator()
-              ->GetSnapPositionForPoint(*box, new_scaled_position,
-                                        scroll_to_options.hasLeft(),
-                                        scroll_to_options.hasTop())
-              .value_or(new_scaled_position);
+    if (SnapCoordinator* coordinator = GetDocument().GetSnapCoordinator()) {
+      new_scaled_position = coordinator->GetSnapPositionForPoint(
+          *box, new_scaled_position, scroll_to_options.hasLeft(),
+          scroll_to_options.hasTop());
     }
     box->ScrollToPosition(new_scaled_position, scroll_behavior);
   }
@@ -1155,14 +1147,10 @@
           box->Style()->EffectiveZoom();
 
     FloatPoint new_scaled_position(scaled_left, scaled_top);
-    if (RuntimeEnabledFeatures::CSSScrollSnapPointsEnabled()) {
-      new_scaled_position =
-          GetDocument()
-              .GetSnapCoordinator()
-              ->GetSnapPositionForPoint(*box, new_scaled_position,
-                                        scroll_to_options.hasLeft(),
-                                        scroll_to_options.hasTop())
-              .value_or(new_scaled_position);
+    if (SnapCoordinator* coordinator = GetDocument().GetSnapCoordinator()) {
+      new_scaled_position = coordinator->GetSnapPositionForPoint(
+          *box, new_scaled_position, scroll_to_options.hasLeft(),
+          scroll_to_options.hasTop());
     }
     box->ScrollToPosition(new_scaled_position, scroll_behavior);
   }
@@ -1201,14 +1189,10 @@
 
   FloatPoint new_scaled_position = viewport->ScrollOffsetToPosition(
       ScrollOffset(new_scaled_left, new_scaled_top));
-  if (RuntimeEnabledFeatures::CSSScrollSnapPointsEnabled()) {
-    new_scaled_position =
-        GetDocument()
-            .GetSnapCoordinator()
-            ->GetSnapPositionForPoint(
-                *GetDocument().GetLayoutView(), new_scaled_position,
-                scroll_to_options.hasLeft(), scroll_to_options.hasTop())
-            .value_or(new_scaled_position);
+  if (SnapCoordinator* coordinator = GetDocument().GetSnapCoordinator()) {
+    new_scaled_position = coordinator->GetSnapPositionForPoint(
+        *GetDocument().GetLayoutView(), new_scaled_position,
+        scroll_to_options.hasLeft(), scroll_to_options.hasTop());
   }
   viewport->SetScrollOffset(
       viewport->ScrollPositionToOffset(new_scaled_position),
@@ -1246,14 +1230,10 @@
 
   FloatPoint new_scaled_position =
       viewport->ScrollOffsetToPosition(ScrollOffset(scaled_left, scaled_top));
-  if (RuntimeEnabledFeatures::CSSScrollSnapPointsEnabled()) {
-    new_scaled_position =
-        GetDocument()
-            .GetSnapCoordinator()
-            ->GetSnapPositionForPoint(
-                *GetDocument().GetLayoutView(), new_scaled_position,
-                scroll_to_options.hasLeft(), scroll_to_options.hasTop())
-            .value_or(new_scaled_position);
+  if (SnapCoordinator* coordinator = GetDocument().GetSnapCoordinator()) {
+    new_scaled_position = coordinator->GetSnapPositionForPoint(
+        *GetDocument().GetLayoutView(), new_scaled_position,
+        scroll_to_options.hasLeft(), scroll_to_options.hasTop());
   }
   viewport->SetScrollOffset(
       viewport->ScrollPositionToOffset(new_scaled_position),
diff --git a/third_party/blink/renderer/core/dom/tree_scope.cc b/third_party/blink/renderer/core/dom/tree_scope.cc
index 07a4e43..fbdd2df 100644
--- a/third_party/blink/renderer/core/dom/tree_scope.cc
+++ b/third_party/blink/renderer/core/dom/tree_scope.cc
@@ -223,8 +223,9 @@
   if (!PointInFrameContentIfVisible(*document, hit_point))
     return HitTestResult();
 
-  HitTestResult result(request, LayoutPoint(hit_point));
-  document->GetLayoutView()->HitTest(result);
+  HitTestLocation location(hit_point);
+  HitTestResult result(request, location);
+  document->GetLayoutView()->HitTest(location, result);
   return result;
 }
 
@@ -314,11 +315,12 @@
   if (!PointInFrameContentIfVisible(document, hit_point))
     return HeapVector<Member<Element>>();
 
+  HitTestLocation location(hit_point);
   HitTestRequest request(HitTestRequest::kReadOnly | HitTestRequest::kActive |
                          HitTestRequest::kListBased |
                          HitTestRequest::kPenetratingList);
-  HitTestResult result(request, LayoutPoint(hit_point));
-  document.GetLayoutView()->HitTest(result);
+  HitTestResult result(request, location);
+  document.GetLayoutView()->HitTest(location, result);
 
   return ElementsFromHitTestResult(result);
 }
diff --git a/third_party/blink/renderer/core/editing/frame_selection.cc b/third_party/blink/renderer/core/editing/frame_selection.cc
index 7b5d78f..eb7e67d6 100644
--- a/third_party/blink/renderer/core/editing/frame_selection.cc
+++ b/third_party/blink/renderer/core/editing/frame_selection.cc
@@ -572,8 +572,9 @@
     return false;
 
   HitTestRequest request(HitTestRequest::kReadOnly | HitTestRequest::kActive);
-  HitTestResult result(request, point);
-  GetDocument().GetLayoutView()->HitTest(result);
+  HitTestLocation location(point);
+  HitTestResult result(request, location);
+  GetDocument().GetLayoutView()->HitTest(location, result);
   Node* inner_node = result.InnerNode();
   if (!inner_node || !inner_node->GetLayoutObject())
     return false;
diff --git a/third_party/blink/renderer/core/editing/selection_controller.cc b/third_party/blink/renderer/core/editing/selection_controller.cc
index 3986cf7..e28832e 100644
--- a/third_party/blink/renderer/core/editing/selection_controller.cc
+++ b/third_party/blink/renderer/core/editing/selection_controller.cc
@@ -993,8 +993,9 @@
     return;
   if (selection_state_ != SelectionState::kExtendedSelection) {
     HitTestRequest request(HitTestRequest::kReadOnly | HitTestRequest::kActive);
-    HitTestResult result(request, mouse_down_pos);
-    frame_->GetDocument()->GetLayoutView()->HitTest(result);
+    HitTestLocation location(mouse_down_pos);
+    HitTestResult result(request, location);
+    frame_->GetDocument()->GetLayoutView()->HitTest(location, result);
 
     UpdateSelectionForMouseDrag(result, drag_start_pos,
                                 last_known_mouse_position);
@@ -1015,9 +1016,9 @@
 
   HitTestRequest request(HitTestRequest::kReadOnly | HitTestRequest::kActive |
                          HitTestRequest::kMove);
-  HitTestResult result(request,
-                       view->ViewportToFrame(last_known_mouse_position));
-  layout_view->HitTest(result);
+  HitTestLocation location(view->ViewportToFrame(last_known_mouse_position));
+  HitTestResult result(request, location);
+  layout_view->HitTest(location, result);
   UpdateSelectionForMouseDrag(result, drag_start_pos,
                               last_known_mouse_position);
 }
diff --git a/third_party/blink/renderer/core/editing/selection_controller_test.cc b/third_party/blink/renderer/core/editing/selection_controller_test.cc
index f8d843f..f07c5c7 100644
--- a/third_party/blink/renderer/core/editing/selection_controller_test.cc
+++ b/third_party/blink/renderer/core/editing/selection_controller_test.cc
@@ -117,8 +117,9 @@
       "  event => elem.parentNode.removeChild(elem));");
   GetDocument().body()->AppendChild(script);
   GetDocument().View()->UpdateAllLifecyclePhases();
+  HitTestLocation location((IntPoint(8, 8)));
   GetFrame().GetEventHandler().GetSelectionController().HandleGestureLongPress(
-      GetFrame().GetEventHandler().HitTestResultAtPoint(IntPoint(8, 8)));
+      GetFrame().GetEventHandler().HitTestResultAtLocation(location));
 }
 
 // For http://crbug.com/704827
@@ -132,8 +133,9 @@
   GetDocument().View()->UpdateAllLifecyclePhases();
 
   // Hit "&nbsp;" in before pseudo element of "sample".
+  HitTestLocation location((IntPoint(10, 10)));
   SetCaretAtHitTestResult(
-      GetFrame().GetEventHandler().HitTestResultAtPoint(IntPoint(10, 10)));
+      GetFrame().GetEventHandler().HitTestResultAtLocation(location));
 
   EXPECT_TRUE(Selection().GetSelectionInDOMTree().IsNone());
 }
@@ -166,10 +168,11 @@
       blink::WebInputEvent::GetStaticTimeStampForTests());
   // Frame scale defaults to 0, which would cause a divide-by-zero problem.
   mouse_event.SetFrameScale(1);
+  HitTestLocation location((IntPoint(0, 0)));
   GetFrame().GetEventHandler().GetSelectionController().HandleMousePressEvent(
       MouseEventWithHitTestResults(
-          mouse_event,
-          GetFrame().GetEventHandler().HitTestResultAtPoint(IntPoint(0, 0))));
+          mouse_event, location,
+          GetFrame().GetEventHandler().HitTestResultAtLocation(location)));
 
   // The original bug was that this test would cause
   // TextSuggestionController::HandlePotentialMisspelledWordTap() to crash. So
diff --git a/third_party/blink/renderer/core/editing/visible_units.cc b/third_party/blink/renderer/core/editing/visible_units.cc
index 3b2bc0b..90fcca9 100644
--- a/third_party/blink/renderer/core/editing/visible_units.cc
+++ b/third_party/blink/renderer/core/editing/visible_units.cc
@@ -569,8 +569,9 @@
   HitTestRequest request = HitTestRequest::kMove | HitTestRequest::kReadOnly |
                            HitTestRequest::kActive |
                            HitTestRequest::kIgnoreClipping;
-  HitTestResult result(request, contents_point);
-  frame->GetDocument()->GetLayoutView()->HitTest(result);
+  HitTestLocation location(contents_point);
+  HitTestResult result(request, location);
+  frame->GetDocument()->GetLayoutView()->HitTest(location, result);
 
   if (Node* node = result.InnerNode()) {
     return CreateVisiblePosition(PositionRespectingEditingBoundary(
diff --git a/third_party/blink/renderer/core/exported/web_dev_tools_agent_impl.cc b/third_party/blink/renderer/core/exported/web_dev_tools_agent_impl.cc
index c3d5777..6a9b05f 100644
--- a/third_party/blink/renderer/core/exported/web_dev_tools_agent_impl.cc
+++ b/third_party/blink/renderer/core/exported/web_dev_tools_agent_impl.cc
@@ -589,10 +589,12 @@
   IntPoint transformed_point = FlooredIntPoint(
       TransformWebMouseEvent(web_local_frame_impl_->GetFrameView(), dummy_event)
           .PositionInRootFrame());
-  HitTestResult result(
-      request, web_local_frame_impl_->GetFrameView()->ConvertFromRootFrame(
-                   transformed_point));
-  web_local_frame_impl_->GetFrame()->ContentLayoutObject()->HitTest(result);
+  HitTestLocation location(
+      web_local_frame_impl_->GetFrameView()->ConvertFromRootFrame(
+          transformed_point));
+  HitTestResult result(request, location);
+  web_local_frame_impl_->GetFrame()->ContentLayoutObject()->HitTest(location,
+                                                                    result);
   Node* node = result.InnerNode();
   if (!node && web_local_frame_impl_->GetFrame()->GetDocument())
     node = web_local_frame_impl_->GetFrame()->GetDocument()->documentElement();
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 2c234ac..ac41703b 100644
--- a/third_party/blink/renderer/core/exported/web_frame_test.cc
+++ b/third_party/blink/renderer/core/exported/web_frame_test.cc
@@ -11642,9 +11642,10 @@
   HitTestRequest request = HitTestRequest::kMove | HitTestRequest::kReadOnly |
                            HitTestRequest::kActive |
                            HitTestRequest::kIgnoreClipping;
-  HitTestResult result(request,
-                       frame_view->ConvertFromRootFrame(LayoutPoint(100, -50)));
-  frame_view->GetLayoutView()->HitTest(result);
+  HitTestLocation location(
+      frame_view->ConvertFromRootFrame(LayoutPoint(100, -50)));
+  HitTestResult result(request, location);
+  frame_view->GetLayoutView()->HitTest(location, result);
 
   EXPECT_EQ(GetDocument().getElementById("top"), result.InnerNode());
 }
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 7833cce..65563af 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
@@ -572,12 +572,13 @@
   if (!frame)
     return false;
 
-  IntPoint location = FrameRect().Location();
-  LayoutRect document_rect(location.X() + rect.x, location.Y() + rect.y,
-                           rect.width, rect.height);
-  HitTestResult result = frame->GetEventHandler().HitTestResultAtRect(
-      document_rect, HitTestRequest::kReadOnly | HitTestRequest::kActive |
-                         HitTestRequest::kListBased);
+  IntPoint frame_location = FrameRect().Location();
+  HitTestLocation location(LayoutRect(frame_location.X() + rect.x,
+                                      frame_location.Y() + rect.y, rect.width,
+                                      rect.height));
+  HitTestResult result = frame->GetEventHandler().HitTestResultAtLocation(
+      location, HitTestRequest::kReadOnly | HitTestRequest::kActive |
+                    HitTestRequest::kListBased);
   const HitTestResult::NodeSet& nodes = result.ListBasedTestResult();
   if (nodes.size() != 1)
     return false;
diff --git a/third_party/blink/renderer/core/exported/web_view_impl.cc b/third_party/blink/renderer/core/exported/web_view_impl.cc
index 4c402ad..beff05c 100644
--- a/third_party/blink/renderer/core/exported/web_view_impl.cc
+++ b/third_party/blink/renderer/core/exported/web_view_impl.cc
@@ -434,14 +434,14 @@
   // Take capture on a mouse down on a plugin so we can send it mouse events.
   // If the hit node is a plugin but a scrollbar is over it don't start mouse
   // capture because it will interfere with the scrollbar receiving events.
-  LayoutPoint point(event.PositionInWidget());
   if (event.button == WebMouseEvent::Button::kLeft &&
       page_->MainFrame()->IsLocalFrame()) {
-    point =
-        page_->DeprecatedLocalMainFrame()->View()->ConvertFromRootFrame(point);
+    HitTestLocation location(
+        page_->DeprecatedLocalMainFrame()->View()->ConvertFromRootFrame(
+            event.PositionInWidget()));
     HitTestResult result(page_->DeprecatedLocalMainFrame()
                              ->GetEventHandler()
-                             .HitTestResultAtPoint(point));
+                             .HitTestResultAtLocation(location));
     result.SetToShadowHostIfInRestrictedShadowRoot();
     Node* hit_node = result.InnerNodeOrImageMapImage();
 
@@ -665,7 +665,7 @@
             // Stash the position of the node that would've been used absent
             // disambiguation, for UMA purposes.
             last_tap_disambiguation_best_candidate_position_ =
-                targeted_event.GetHitTestResult().RoundedPointInMainFrame() -
+                RoundedIntPoint(targeted_event.GetHitTestLocation().Point()) -
                 RoundedIntSize(targeted_event.GetHitTestResult().LocalPoint());
 
             EnableTapHighlights(highlight_nodes);
@@ -790,7 +790,7 @@
         page_->DeprecatedLocalMainFrame()->GetEventHandler().TargetGestureEvent(
             scaled_event);
     WebPoint node_position =
-        targeted_event.GetHitTestResult().RoundedPointInMainFrame() -
+        RoundedIntPoint(targeted_event.GetHitTestLocation().Point()) -
         RoundedIntSize(targeted_event.GetHitTestResult().LocalPoint());
     TapDisambiguationResult result =
         (node_position == last_tap_disambiguation_best_candidate_position_)
@@ -1030,14 +1030,15 @@
     return WebRect();
 
   // Use the point-based hit test to find the node.
-  LayoutPoint point = MainFrameImpl()->GetFrameView()->ConvertFromRootFrame(
-      LayoutPoint(point_in_root_frame));
+  HitTestLocation location(
+      MainFrameImpl()->GetFrameView()->ConvertFromRootFrame(
+          LayoutPoint(point_in_root_frame)));
   HitTestRequest::HitTestRequestType hit_type =
       HitTestRequest::kReadOnly | HitTestRequest::kActive |
       (ignore_clipping ? HitTestRequest::kIgnoreClipping : 0);
   HitTestResult result =
-      MainFrameImpl()->GetFrame()->GetEventHandler().HitTestResultAtPoint(
-          point, hit_type);
+      MainFrameImpl()->GetFrame()->GetEventHandler().HitTestResultAtLocation(
+          location, hit_type);
   result.SetToShadowHostIfInRestrictedShadowRoot();
 
   Node* node = result.InnerNodeOrImageMapImage();
@@ -1909,7 +1910,6 @@
   // routing code.
   if (!MainFrameImpl())
     return WebInputEventResult::kNotHandled;
-
   DCHECK(!WebInputEvent::IsTouchEventType(input_event.GetType()));
 
   GetPage()->GetVisualViewport().StartTrackingPinchStats();
@@ -3287,12 +3287,14 @@
     const LayoutPoint& pos_in_root_frame) {
   if (!page_->MainFrame()->IsLocalFrame())
     return HitTestResult();
-  LayoutPoint doc_point(
+  HitTestLocation location(
       page_->DeprecatedLocalMainFrame()->View()->ConvertFromRootFrame(
           pos_in_root_frame));
   HitTestResult result =
-      page_->DeprecatedLocalMainFrame()->GetEventHandler().HitTestResultAtPoint(
-          doc_point, HitTestRequest::kReadOnly | HitTestRequest::kActive);
+      page_->DeprecatedLocalMainFrame()
+          ->GetEventHandler()
+          .HitTestResultAtLocation(
+              location, HitTestRequest::kReadOnly | HitTestRequest::kActive);
   result.SetToShadowHostIfInRestrictedShadowRoot();
   return result;
 }
diff --git a/third_party/blink/renderer/core/fetch/blob_bytes_consumer.cc b/third_party/blink/renderer/core/fetch/blob_bytes_consumer.cc
index bfa9f02..17a17f5 100644
--- a/third_party/blink/renderer/core/fetch/blob_bytes_consumer.cc
+++ b/third_party/blink/renderer/core/fetch/blob_bytes_consumer.cc
@@ -4,132 +4,52 @@
 
 #include "third_party/blink/renderer/core/fetch/blob_bytes_consumer.h"
 
-#include "third_party/blink/renderer/core/fetch/bytes_consumer_for_data_consumer_handle.h"
-#include "third_party/blink/renderer/core/loader/threadable_loader.h"
+#include "third_party/blink/renderer/core/execution_context/execution_context.h"
+#include "third_party/blink/renderer/core/fetch/form_data_bytes_consumer.h"
 #include "third_party/blink/renderer/platform/blob/blob_data.h"
-#include "third_party/blink/renderer/platform/blob/blob_registry.h"
-#include "third_party/blink/renderer/platform/blob/blob_url.h"
-#include "third_party/blink/renderer/platform/loader/fetch/fetch_initiator_type_names.h"
-#include "third_party/blink/renderer/platform/loader/fetch/resource_error.h"
-#include "third_party/blink/renderer/platform/loader/fetch/resource_loader_options.h"
-#include "third_party/blink/renderer/platform/loader/fetch/resource_request.h"
-#include "third_party/blink/renderer/platform/weborigin/kurl.h"
-#include "third_party/blink/renderer/platform/weborigin/security_origin.h"
 
 namespace blink {
 
 BlobBytesConsumer::BlobBytesConsumer(
     ExecutionContext* execution_context,
-    scoped_refptr<BlobDataHandle> blob_data_handle,
-    ThreadableLoader* loader)
-    : ContextLifecycleObserver(execution_context),
-      blob_data_handle_(std::move(blob_data_handle)),
-      loader_(loader) {
-  if (!blob_data_handle_) {
-    // Note that |m_loader| is non-null only in tests.
-    if (loader_) {
-      loader_->Cancel();
-      loader_ = nullptr;
-    }
-    state_ = PublicState::kClosed;
-  }
-}
-
-BlobBytesConsumer::BlobBytesConsumer(
-    ExecutionContext* execution_context,
     scoped_refptr<BlobDataHandle> blob_data_handle)
-    : BlobBytesConsumer(execution_context,
-                        std::move(blob_data_handle),
-                        nullptr) {}
+    : execution_context_(execution_context),
+      blob_data_handle_(std::move(blob_data_handle)) {}
 
 BlobBytesConsumer::~BlobBytesConsumer() {
-  if (!blob_url_.IsEmpty())
-    BlobRegistry::RevokePublicBlobURL(blob_url_);
 }
 
 BytesConsumer::Result BlobBytesConsumer::BeginRead(const char** buffer,
                                                    size_t* available) {
-  *buffer = nullptr;
-  *available = 0;
+  if (!nested_consumer_) {
+    if (!blob_data_handle_)
+      return Result::kDone;
 
-  if (state_ == PublicState::kClosed) {
-    // It's possible that |cancel| has been called before the first
-    // |beginRead| call. That's why we need to check this condition
-    // before checking |isClean()|.
-    return Result::kDone;
-  }
-
-  if (IsClean()) {
-    DCHECK(blob_url_.IsEmpty());
-    blob_url_ =
-        BlobURL::CreatePublicURL(GetExecutionContext()->GetSecurityOrigin());
-    if (blob_url_.IsEmpty()) {
-      GetError();
-    } else {
-      BlobRegistry::RegisterPublicBlobURL(
-          GetExecutionContext()->GetMutableSecurityOrigin(), blob_url_,
-          blob_data_handle_);
-
-      // m_loader is non-null only in tests.
-      if (!loader_)
-        loader_ = CreateLoader();
-
-      ResourceRequest request(blob_url_);
-      request.SetRequestContext(WebURLRequest::kRequestContextInternal);
-      request.SetFetchRequestMode(
-          network::mojom::FetchRequestMode::kSameOrigin);
-      request.SetFetchCredentialsMode(
-          network::mojom::FetchCredentialsMode::kOmit);
-      request.SetUseStreamOnResponse(true);
-      // We intentionally skip
-      // 'setExternalRequestStateFromRequestorAddressSpace', as 'blob:'
-      // can never be external.
-      loader_->Start(request);
-    }
+    scoped_refptr<EncodedFormData> form_data = EncodedFormData::Create();
+    form_data->AppendDataPipe(base::MakeRefCounted<WrappedDataPipeGetter>(
+        blob_data_handle_->AsDataPipeGetter()));
+    nested_consumer_ =
+        new FormDataBytesConsumer(execution_context_, std::move(form_data));
+    if (client_)
+      nested_consumer_->SetClient(client_);
     blob_data_handle_ = nullptr;
+    client_ = nullptr;
   }
-  DCHECK_NE(state_, PublicState::kClosed);
-
-  if (state_ == PublicState::kErrored)
-    return Result::kError;
-
-  if (!body_) {
-    // The response has not arrived.
-    return Result::kShouldWait;
-  }
-
-  auto result = body_->BeginRead(buffer, available);
-  switch (result) {
-    case Result::kOk:
-    case Result::kShouldWait:
-      break;
-    case Result::kDone:
-      has_seen_end_of_data_ = true;
-      if (has_finished_loading_)
-        Close();
-      return state_ == PublicState::kClosed ? Result::kDone
-                                            : Result::kShouldWait;
-    case Result::kError:
-      GetError();
-      break;
-  }
-  return result;
+  return nested_consumer_->BeginRead(buffer, available);
 }
 
 BytesConsumer::Result BlobBytesConsumer::EndRead(size_t read) {
-  DCHECK(body_);
-  return body_->EndRead(read);
+  DCHECK(nested_consumer_);
+  return nested_consumer_->EndRead(read);
 }
 
 scoped_refptr<BlobDataHandle> BlobBytesConsumer::DrainAsBlobDataHandle(
     BlobSizePolicy policy) {
-  if (!IsClean())
+  if (!blob_data_handle_)
     return nullptr;
-  DCHECK(blob_data_handle_);
   if (policy == BlobSizePolicy::kDisallowBlobWithInvalidSize &&
       blob_data_handle_->size() == UINT64_MAX)
     return nullptr;
-  Close();
   return std::move(blob_data_handle_);
 }
 
@@ -146,172 +66,43 @@
 void BlobBytesConsumer::SetClient(BytesConsumer::Client* client) {
   DCHECK(!client_);
   DCHECK(client);
-  client_ = client;
+  if (nested_consumer_)
+    nested_consumer_->SetClient(client);
+  else
+    client_ = client;
 }
 
 void BlobBytesConsumer::ClearClient() {
   client_ = nullptr;
+  if (nested_consumer_)
+    nested_consumer_->ClearClient();
 }
 
 void BlobBytesConsumer::Cancel() {
-  if (state_ == PublicState::kClosed || state_ == PublicState::kErrored)
-    return;
-  Close();
-  if (body_) {
-    body_->Cancel();
-    body_ = nullptr;
-  }
-  if (!blob_url_.IsEmpty()) {
-    BlobRegistry::RevokePublicBlobURL(blob_url_);
-    blob_url_ = KURL();
-  }
+  if (nested_consumer_)
+    nested_consumer_->Cancel();
   blob_data_handle_ = nullptr;
+  client_ = nullptr;
 }
 
 BytesConsumer::Error BlobBytesConsumer::GetError() const {
-  DCHECK_EQ(PublicState::kErrored, state_);
-  return Error("Failed to load a blob.");
+  DCHECK(nested_consumer_);
+  return nested_consumer_->GetError();
 }
 
 BytesConsumer::PublicState BlobBytesConsumer::GetPublicState() const {
-  return state_;
-}
-
-void BlobBytesConsumer::ContextDestroyed(ExecutionContext*) {
-  if (state_ != PublicState::kReadableOrWaiting)
-    return;
-
-  BytesConsumer::Client* client = client_;
-  GetError();
-  if (client)
-    client->OnStateChange();
-}
-
-void BlobBytesConsumer::OnStateChange() {
-  if (state_ != PublicState::kReadableOrWaiting)
-    return;
-  DCHECK(body_);
-
-  BytesConsumer::Client* client = client_;
-  switch (body_->GetPublicState()) {
-    case PublicState::kReadableOrWaiting:
-      break;
-    case PublicState::kClosed:
-      has_seen_end_of_data_ = true;
-      if (has_finished_loading_)
-        Close();
-      break;
-    case PublicState::kErrored:
-      GetError();
-      break;
+  if (!nested_consumer_) {
+    return blob_data_handle_ ? PublicState::kReadableOrWaiting
+                             : PublicState::kClosed;
   }
-  if (client)
-    client->OnStateChange();
-}
-
-void BlobBytesConsumer::DidReceiveResponse(
-    unsigned long identifier,
-    const ResourceResponse&,
-    std::unique_ptr<WebDataConsumerHandle> handle) {
-  DCHECK(handle);
-  DCHECK(!body_);
-  DCHECK_EQ(PublicState::kReadableOrWaiting, state_);
-
-  body_ = new BytesConsumerForDataConsumerHandle(GetExecutionContext(),
-                                                 std::move(handle));
-  body_->SetClient(this);
-
-  if (IsClean()) {
-    // This function is called synchronously in ThreadableLoader::start.
-    return;
-  }
-  OnStateChange();
-}
-
-void BlobBytesConsumer::DidFinishLoading(unsigned long identifier) {
-  DCHECK_EQ(PublicState::kReadableOrWaiting, state_);
-  has_finished_loading_ = true;
-  loader_ = nullptr;
-  if (!has_seen_end_of_data_)
-    return;
-  DCHECK(!IsClean());
-  BytesConsumer::Client* client = client_;
-  Close();
-  if (client)
-    client->OnStateChange();
-}
-
-void BlobBytesConsumer::DidFail(const ResourceError& e) {
-  if (e.IsCancellation()) {
-    if (state_ != PublicState::kReadableOrWaiting)
-      return;
-  }
-  DCHECK_EQ(PublicState::kReadableOrWaiting, state_);
-  loader_ = nullptr;
-  BytesConsumer::Client* client = client_;
-  GetError();
-  if (IsClean()) {
-    // This function is called synchronously in ThreadableLoader::start.
-    return;
-  }
-  if (client) {
-    client->OnStateChange();
-    client = nullptr;
-  }
-}
-
-void BlobBytesConsumer::DidFailRedirectCheck() {
-  NOTREACHED();
+  return nested_consumer_->GetPublicState();
 }
 
 void BlobBytesConsumer::Trace(blink::Visitor* visitor) {
-  visitor->Trace(body_);
+  visitor->Trace(execution_context_);
+  visitor->Trace(nested_consumer_);
   visitor->Trace(client_);
-  visitor->Trace(loader_);
   BytesConsumer::Trace(visitor);
-  BytesConsumer::Client::Trace(visitor);
-  ContextLifecycleObserver::Trace(visitor);
-}
-
-BlobBytesConsumer* BlobBytesConsumer::CreateForTesting(
-    ExecutionContext* execution_context,
-    scoped_refptr<BlobDataHandle> blob_data_handle,
-    ThreadableLoader* loader) {
-  return new BlobBytesConsumer(execution_context, std::move(blob_data_handle),
-                               loader);
-}
-
-ThreadableLoader* BlobBytesConsumer::CreateLoader() {
-  ThreadableLoaderOptions options;
-
-  ResourceLoaderOptions resource_loader_options;
-  resource_loader_options.data_buffering_policy = kDoNotBufferData;
-  resource_loader_options.initiator_info.name =
-      FetchInitiatorTypeNames::internal;
-
-  return ThreadableLoader::Create(*GetExecutionContext(), this, options,
-                                  resource_loader_options);
-}
-
-void BlobBytesConsumer::Close() {
-  DCHECK_EQ(state_, PublicState::kReadableOrWaiting);
-  state_ = PublicState::kClosed;
-  Clear();
-}
-
-void BlobBytesConsumer::GetError() {
-  DCHECK_EQ(state_, PublicState::kReadableOrWaiting);
-  state_ = PublicState::kErrored;
-  Clear();
-}
-
-void BlobBytesConsumer::Clear() {
-  DCHECK_NE(state_, PublicState::kReadableOrWaiting);
-  if (loader_) {
-    loader_->Cancel();
-    loader_ = nullptr;
-  }
-  client_ = nullptr;
 }
 
 }  // namespace blink
diff --git a/third_party/blink/renderer/core/fetch/blob_bytes_consumer.h b/third_party/blink/renderer/core/fetch/blob_bytes_consumer.h
index 6743053..57927398 100644
--- a/third_party/blink/renderer/core/fetch/blob_bytes_consumer.h
+++ b/third_party/blink/renderer/core/fetch/blob_bytes_consumer.h
@@ -8,28 +8,17 @@
 #include <memory>
 #include "base/memory/scoped_refptr.h"
 #include "third_party/blink/renderer/core/core_export.h"
-#include "third_party/blink/renderer/core/dom/context_lifecycle_observer.h"
 #include "third_party/blink/renderer/core/fetch/bytes_consumer.h"
-#include "third_party/blink/renderer/core/loader/threadable_loader_client.h"
 #include "third_party/blink/renderer/platform/heap/handle.h"
 
 namespace blink {
 
 class BlobDataHandle;
-class EncodedFormData;
 class ExecutionContext;
-class ThreadableLoader;
-class WebDataConsumerHandle;
 
 // A BlobBytesConsumer is created from a blob handle and it will
 // return a valid handle from drainAsBlobDataHandle as much as possible.
-class CORE_EXPORT BlobBytesConsumer final : public BytesConsumer,
-                                            public ContextLifecycleObserver,
-                                            public BytesConsumer::Client,
-                                            public ThreadableLoaderClient {
-  USING_GARBAGE_COLLECTED_MIXIN(BlobBytesConsumer);
-  USING_PRE_FINALIZER(BlobBytesConsumer, Cancel);
-
+class CORE_EXPORT BlobBytesConsumer final : public BytesConsumer {
  public:
   // |handle| can be null. In that case this consumer gets closed.
   BlobBytesConsumer(ExecutionContext*,
@@ -48,47 +37,13 @@
   Error GetError() const override;
   String DebugName() const override { return "BlobBytesConsumer"; }
 
-  // ContextLifecycleObserver implementation
-  void ContextDestroyed(ExecutionContext*) override;
-
-  // BytesConsumer::Client implementation
-  void OnStateChange() override;
-
-  // ThreadableLoaderClient implementation
-  void DidReceiveResponse(unsigned long identifier,
-                          const ResourceResponse&,
-                          std::unique_ptr<WebDataConsumerHandle>) override;
-  void DidFinishLoading(unsigned long identifier) override;
-  void DidFail(const ResourceError&) override;
-  void DidFailRedirectCheck() override;
-
   void Trace(blink::Visitor*) override;
 
-  static BlobBytesConsumer* CreateForTesting(ExecutionContext*,
-                                             scoped_refptr<BlobDataHandle>,
-                                             ThreadableLoader*);
-
  private:
-  BlobBytesConsumer(ExecutionContext*,
-                    scoped_refptr<BlobDataHandle>,
-                    ThreadableLoader*);
-  ThreadableLoader* CreateLoader();
-  void DidFailInternal();
-  bool IsClean() const { return blob_data_handle_.get(); }
-  void Close();
-  void GetError();
-  void Clear();
-
-  KURL blob_url_;
+  Member<ExecutionContext> execution_context_;
   scoped_refptr<BlobDataHandle> blob_data_handle_;
-  Member<BytesConsumer> body_;
+  Member<BytesConsumer> nested_consumer_;
   Member<BytesConsumer::Client> client_;
-  Member<ThreadableLoader> loader_;
-
-  PublicState state_ = PublicState::kReadableOrWaiting;
-  // These two booleans are meaningful only when m_state is ReadableOrWaiting.
-  bool has_seen_end_of_data_ = false;
-  bool has_finished_loading_ = false;
 };
 
 }  // namespace blink
diff --git a/third_party/blink/renderer/core/fetch/blob_bytes_consumer_test.cc b/third_party/blink/renderer/core/fetch/blob_bytes_consumer_test.cc
index 8778011..8a2581ef 100644
--- a/third_party/blink/renderer/core/fetch/blob_bytes_consumer_test.cc
+++ b/third_party/blink/renderer/core/fetch/blob_bytes_consumer_test.cc
@@ -4,12 +4,14 @@
 
 #include "third_party/blink/renderer/core/fetch/blob_bytes_consumer.h"
 
+#include "mojo/public/cpp/bindings/strong_binding.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "third_party/blink/renderer/core/fetch/bytes_consumer_test_util.h"
 #include "third_party/blink/renderer/core/fetch/data_consumer_handle_test_util.h"
 #include "third_party/blink/renderer/core/loader/threadable_loader.h"
 #include "third_party/blink/renderer/core/testing/page_test_base.h"
 #include "third_party/blink/renderer/platform/blob/blob_data.h"
+#include "third_party/blink/renderer/platform/blob/testing/fake_blob.h"
 #include "third_party/blink/renderer/platform/loader/fetch/resource_error.h"
 #include "third_party/blink/renderer/platform/loader/fetch/resource_response.h"
 #include "third_party/blink/renderer/platform/network/encoded_form_data.h"
@@ -25,92 +27,6 @@
 using ReplayingHandle = DataConsumerHandleTestUtil::ReplayingHandle;
 using Result = BytesConsumer::Result;
 
-class TestThreadableLoader : public ThreadableLoader {
- public:
-  ~TestThreadableLoader() override {
-    EXPECT_FALSE(should_be_cancelled_ && !is_cancelled_)
-        << "The loader should be cancelled but is not cancelled.";
-  }
-
-  void Start(const ResourceRequest& request) override { is_started_ = true; }
-
-  void OverrideTimeout(unsigned long timeout_milliseconds) override {
-    ADD_FAILURE() << "overrideTimeout should not be called.";
-  }
-
-  void Cancel() override { is_cancelled_ = true; }
-  void Detach() override { NOTREACHED(); }
-
-  bool IsStarted() const { return is_started_; }
-  bool IsCancelled() const { return is_cancelled_; }
-  void SetShouldBeCancelled() { should_be_cancelled_ = true; }
-
- private:
-  bool is_started_ = false;
-  bool is_cancelled_ = false;
-  bool should_be_cancelled_ = false;
-};
-
-class SyncLoadingTestThreadableLoader : public ThreadableLoader {
- public:
-  ~SyncLoadingTestThreadableLoader() override { DCHECK(!handle_); }
-
-  void Start(const ResourceRequest& request) override {
-    is_started_ = true;
-    client_->DidReceiveResponse(0, ResourceResponse(), std::move(handle_));
-    client_->DidFinishLoading(0);
-  }
-
-  void OverrideTimeout(unsigned long timeout_milliseconds) override {
-    ADD_FAILURE() << "overrideTimeout should not be called.";
-  }
-
-  void Cancel() override { is_cancelled_ = true; }
-  void Detach() override { NOTREACHED(); }
-
-  bool IsStarted() const { return is_started_; }
-  bool IsCancelled() const { return is_cancelled_; }
-
-  void SetClient(ThreadableLoaderClient* client) { client_ = client; }
-
-  void SetHandle(std::unique_ptr<WebDataConsumerHandle> handle) {
-    handle_ = std::move(handle);
-  }
-
- private:
-  bool is_started_ = false;
-  bool is_cancelled_ = false;
-  ThreadableLoaderClient* client_ = nullptr;
-  std::unique_ptr<WebDataConsumerHandle> handle_;
-};
-
-class SyncErrorTestThreadableLoader : public ThreadableLoader {
- public:
-  ~SyncErrorTestThreadableLoader() override {}
-
-  void Start(const ResourceRequest& request) override {
-    is_started_ = true;
-    client_->DidFail(ResourceError::Failure(NullURL()));
-  }
-
-  void OverrideTimeout(unsigned long timeout_milliseconds) override {
-    ADD_FAILURE() << "overrideTimeout should not be called.";
-  }
-
-  void Cancel() override { is_cancelled_ = true; }
-  void Detach() override { NOTREACHED(); }
-
-  bool IsStarted() const { return is_started_; }
-  bool IsCancelled() const { return is_cancelled_; }
-
-  void SetClient(ThreadableLoaderClient* client) { client_ = client; }
-
- private:
-  bool is_started_ = false;
-  bool is_cancelled_ = false;
-  ThreadableLoaderClient* client_ = nullptr;
-};
-
 class BlobBytesConsumerTestClient final
     : public GarbageCollectedFinalized<BlobBytesConsumerTestClient>,
       public BytesConsumer::Client {
@@ -128,144 +44,71 @@
 class BlobBytesConsumerTest : public PageTestBase {
  public:
   void SetUp() override { PageTestBase::SetUp(IntSize(1, 1)); }
+  scoped_refptr<BlobDataHandle> CreateBlob(const String& body) {
+    mojom::blink::BlobPtrInfo mojo_blob;
+    mojo::MakeStrongBinding(
+        std::make_unique<FakeBlob>(kBlobUUID, body, &blob_state_),
+        MakeRequest(&mojo_blob));
+    return BlobDataHandle::Create(kBlobUUID, "", body.length(),
+                                  std::move(mojo_blob));
+  }
+
+  bool DidStartLoading() {
+    base::RunLoop().RunUntilIdle();
+    return blob_state_.did_initiate_read_operation;
+  }
+
+ private:
+  const String kBlobUUID = "blob-id";
+  FakeBlob::State blob_state_;
 };
 
 TEST_F(BlobBytesConsumerTest, TwoPhaseRead) {
-  scoped_refptr<BlobDataHandle> blob_data_handle =
-      BlobDataHandle::Create(BlobData::Create(), 12345);
-  TestThreadableLoader* loader = new TestThreadableLoader();
-  BlobBytesConsumer* consumer = BlobBytesConsumer::CreateForTesting(
-      &GetDocument(), blob_data_handle, loader);
-  std::unique_ptr<ReplayingHandle> src = ReplayingHandle::Create();
-  src->Add(DataConsumerCommand(DataConsumerCommand::kData, "hello, "));
-  src->Add(DataConsumerCommand(DataConsumerCommand::kWait));
-  src->Add(DataConsumerCommand(DataConsumerCommand::kData, "world"));
-  src->Add(DataConsumerCommand(DataConsumerCommand::kDone));
+  String body = "hello, world";
+  scoped_refptr<BlobDataHandle> blob_data_handle = CreateBlob(body);
+
+  BlobBytesConsumer* consumer =
+      new BlobBytesConsumer(&GetDocument(), blob_data_handle);
 
   EXPECT_EQ(PublicState::kReadableOrWaiting, consumer->GetPublicState());
-  EXPECT_FALSE(loader->IsStarted());
+  EXPECT_FALSE(DidStartLoading());
 
   const char* buffer = nullptr;
   size_t available = 0;
   EXPECT_EQ(Result::kShouldWait, consumer->BeginRead(&buffer, &available));
-  EXPECT_TRUE(loader->IsStarted());
+  EXPECT_TRUE(DidStartLoading());
   EXPECT_FALSE(consumer->DrainAsBlobDataHandle(
       BytesConsumer::BlobSizePolicy::kAllowBlobWithInvalidSize));
   EXPECT_FALSE(consumer->DrainAsFormData());
   EXPECT_EQ(PublicState::kReadableOrWaiting, consumer->GetPublicState());
 
-  consumer->DidReceiveResponse(0, ResourceResponse(), std::move(src));
-  consumer->DidFinishLoading(0);
-
   auto result = (new BytesConsumerTestUtil::TwoPhaseReader(consumer))->Run();
   EXPECT_EQ(Result::kDone, result.first);
   EXPECT_EQ("hello, world",
             BytesConsumerTestUtil::CharVectorToString(result.second));
 }
 
-TEST_F(BlobBytesConsumerTest, FailLoading) {
-  scoped_refptr<BlobDataHandle> blob_data_handle =
-      BlobDataHandle::Create(BlobData::Create(), 12345);
-  TestThreadableLoader* loader = new TestThreadableLoader();
-  BlobBytesConsumer* consumer = BlobBytesConsumer::CreateForTesting(
-      &GetDocument(), blob_data_handle, loader);
-  BlobBytesConsumerTestClient* client = new BlobBytesConsumerTestClient();
-  consumer->SetClient(client);
-
-  const char* buffer = nullptr;
-  size_t available = 0;
-  EXPECT_EQ(Result::kShouldWait, consumer->BeginRead(&buffer, &available));
-  EXPECT_TRUE(loader->IsStarted());
-  EXPECT_EQ(PublicState::kReadableOrWaiting, consumer->GetPublicState());
-
-  int num_on_state_change_called = client->NumOnStateChangeCalled();
-  consumer->DidFail(ResourceError::Failure(NullURL()));
-
-  EXPECT_EQ(num_on_state_change_called + 1, client->NumOnStateChangeCalled());
-  EXPECT_EQ(PublicState::kErrored, consumer->GetPublicState());
-  EXPECT_EQ(Result::kError, consumer->BeginRead(&buffer, &available));
-}
-
-TEST_F(BlobBytesConsumerTest, FailLoadingAfterResponseReceived) {
-  scoped_refptr<BlobDataHandle> blob_data_handle =
-      BlobDataHandle::Create(BlobData::Create(), 12345);
-  TestThreadableLoader* loader = new TestThreadableLoader();
-  BlobBytesConsumer* consumer = BlobBytesConsumer::CreateForTesting(
-      &GetDocument(), blob_data_handle, loader);
-  BlobBytesConsumerTestClient* client = new BlobBytesConsumerTestClient();
-  consumer->SetClient(client);
-
-  const char* buffer = nullptr;
-  size_t available;
-  EXPECT_EQ(Result::kShouldWait, consumer->BeginRead(&buffer, &available));
-  EXPECT_TRUE(loader->IsStarted());
-  EXPECT_EQ(PublicState::kReadableOrWaiting, consumer->GetPublicState());
-
-  int num_on_state_change_called = client->NumOnStateChangeCalled();
-  consumer->DidReceiveResponse(
-      0, ResourceResponse(),
-      DataConsumerHandleTestUtil::CreateWaitingDataConsumerHandle());
-  EXPECT_EQ(num_on_state_change_called + 1, client->NumOnStateChangeCalled());
-  EXPECT_EQ(Result::kShouldWait, consumer->BeginRead(&buffer, &available));
-  EXPECT_EQ(PublicState::kReadableOrWaiting, consumer->GetPublicState());
-
-  consumer->DidFail(ResourceError::Failure(NullURL()));
-  EXPECT_EQ(num_on_state_change_called + 2, client->NumOnStateChangeCalled());
-  EXPECT_EQ(PublicState::kErrored, consumer->GetPublicState());
-  EXPECT_EQ(Result::kError, consumer->BeginRead(&buffer, &available));
-}
-
-TEST_F(BlobBytesConsumerTest, FailAccessControlCheck) {
-  scoped_refptr<BlobDataHandle> blob_data_handle =
-      BlobDataHandle::Create(BlobData::Create(), 12345);
-  TestThreadableLoader* loader = new TestThreadableLoader();
-  BlobBytesConsumer* consumer = BlobBytesConsumer::CreateForTesting(
-      &GetDocument(), blob_data_handle, loader);
-  BlobBytesConsumerTestClient* client = new BlobBytesConsumerTestClient();
-  consumer->SetClient(client);
-
-  const char* buffer = nullptr;
-  size_t available;
-  EXPECT_EQ(Result::kShouldWait, consumer->BeginRead(&buffer, &available));
-  EXPECT_TRUE(loader->IsStarted());
-  EXPECT_EQ(PublicState::kReadableOrWaiting, consumer->GetPublicState());
-
-  int num_on_state_change_called = client->NumOnStateChangeCalled();
-  consumer->DidFail(ResourceError::Failure(NullURL()));
-  EXPECT_EQ(num_on_state_change_called + 1, client->NumOnStateChangeCalled());
-
-  EXPECT_EQ(PublicState::kErrored, consumer->GetPublicState());
-  EXPECT_EQ(Result::kError, consumer->BeginRead(&buffer, &available));
-}
-
 TEST_F(BlobBytesConsumerTest, CancelBeforeStarting) {
-  scoped_refptr<BlobDataHandle> blob_data_handle =
-      BlobDataHandle::Create(BlobData::Create(), 12345);
-  TestThreadableLoader* loader = new TestThreadableLoader();
-  BlobBytesConsumer* consumer = BlobBytesConsumer::CreateForTesting(
-      &GetDocument(), blob_data_handle, loader);
+  scoped_refptr<BlobDataHandle> blob_data_handle = CreateBlob("foo bar");
+  BlobBytesConsumer* consumer =
+      new BlobBytesConsumer(&GetDocument(), blob_data_handle);
   BlobBytesConsumerTestClient* client = new BlobBytesConsumerTestClient();
   consumer->SetClient(client);
 
   consumer->Cancel();
-  // This should be FALSE in production, but TRUE here because we set the
-  // loader before starting loading in tests.
-  EXPECT_TRUE(loader->IsCancelled());
 
   const char* buffer = nullptr;
   size_t available;
   EXPECT_EQ(Result::kDone, consumer->BeginRead(&buffer, &available));
   EXPECT_EQ(PublicState::kClosed, consumer->GetPublicState());
-  EXPECT_FALSE(loader->IsStarted());
+  EXPECT_FALSE(DidStartLoading());
   EXPECT_EQ(0, client->NumOnStateChangeCalled());
 }
 
 TEST_F(BlobBytesConsumerTest, CancelAfterStarting) {
-  scoped_refptr<BlobDataHandle> blob_data_handle =
-      BlobDataHandle::Create(BlobData::Create(), 12345);
-  TestThreadableLoader* loader = new TestThreadableLoader();
-  BlobBytesConsumer* consumer = BlobBytesConsumer::CreateForTesting(
-      &GetDocument(), blob_data_handle, loader);
+  scoped_refptr<BlobDataHandle> blob_data_handle = CreateBlob("foo bar");
+  BlobBytesConsumer* consumer =
+      new BlobBytesConsumer(&GetDocument(), blob_data_handle);
   BlobBytesConsumerTestClient* client = new BlobBytesConsumerTestClient();
   consumer->SetClient(client);
 
@@ -273,130 +116,43 @@
   size_t available;
   EXPECT_EQ(Result::kShouldWait, consumer->BeginRead(&buffer, &available));
   EXPECT_EQ(PublicState::kReadableOrWaiting, consumer->GetPublicState());
-  EXPECT_TRUE(loader->IsStarted());
   EXPECT_EQ(0, client->NumOnStateChangeCalled());
 
   consumer->Cancel();
-  EXPECT_TRUE(loader->IsCancelled());
   EXPECT_EQ(PublicState::kClosed, consumer->GetPublicState());
   EXPECT_EQ(Result::kDone, consumer->BeginRead(&buffer, &available));
   EXPECT_EQ(0, client->NumOnStateChangeCalled());
-}
-
-TEST_F(BlobBytesConsumerTest, ReadLastChunkBeforeDidFinishLoadingArrives) {
-  scoped_refptr<BlobDataHandle> blob_data_handle =
-      BlobDataHandle::Create(BlobData::Create(), 12345);
-  TestThreadableLoader* loader = new TestThreadableLoader();
-  BlobBytesConsumer* consumer = BlobBytesConsumer::CreateForTesting(
-      &GetDocument(), blob_data_handle, loader);
-  BlobBytesConsumerTestClient* client = new BlobBytesConsumerTestClient();
-  consumer->SetClient(client);
-  std::unique_ptr<ReplayingHandle> src = ReplayingHandle::Create();
-  src->Add(DataConsumerCommand(DataConsumerCommand::kData, "hello"));
-  src->Add(DataConsumerCommand(DataConsumerCommand::kDone));
-
-  EXPECT_EQ(PublicState::kReadableOrWaiting, consumer->GetPublicState());
-  EXPECT_FALSE(loader->IsStarted());
-
-  const char* buffer = nullptr;
-  size_t available;
-  EXPECT_EQ(Result::kShouldWait, consumer->BeginRead(&buffer, &available));
-  EXPECT_EQ(PublicState::kReadableOrWaiting, consumer->GetPublicState());
-  EXPECT_TRUE(loader->IsStarted());
-  EXPECT_EQ(0, client->NumOnStateChangeCalled());
-
-  consumer->DidReceiveResponse(0, ResourceResponse(), std::move(src));
-  EXPECT_EQ(1, client->NumOnStateChangeCalled());
-  test::RunPendingTasks();
-  EXPECT_EQ(2, client->NumOnStateChangeCalled());
-
-  EXPECT_EQ(PublicState::kReadableOrWaiting, consumer->GetPublicState());
-  ASSERT_EQ(Result::kOk, consumer->BeginRead(&buffer, &available));
-  ASSERT_EQ(5u, available);
-  EXPECT_EQ("hello", String(buffer, available));
-  ASSERT_EQ(Result::kOk, consumer->EndRead(available));
-
-  EXPECT_EQ(PublicState::kReadableOrWaiting, consumer->GetPublicState());
-  ASSERT_EQ(Result::kShouldWait, consumer->BeginRead(&buffer, &available));
-  EXPECT_EQ(PublicState::kReadableOrWaiting, consumer->GetPublicState());
-
-  consumer->DidFinishLoading(0);
-  EXPECT_EQ(3, client->NumOnStateChangeCalled());
-  EXPECT_EQ(PublicState::kClosed, consumer->GetPublicState());
-  ASSERT_EQ(Result::kDone, consumer->BeginRead(&buffer, &available));
-}
-
-TEST_F(BlobBytesConsumerTest, ReadLastChunkAfterDidFinishLoadingArrives) {
-  scoped_refptr<BlobDataHandle> blob_data_handle =
-      BlobDataHandle::Create(BlobData::Create(), 12345);
-  TestThreadableLoader* loader = new TestThreadableLoader();
-  BlobBytesConsumer* consumer = BlobBytesConsumer::CreateForTesting(
-      &GetDocument(), blob_data_handle, loader);
-  BlobBytesConsumerTestClient* client = new BlobBytesConsumerTestClient();
-  consumer->SetClient(client);
-  std::unique_ptr<ReplayingHandle> src = ReplayingHandle::Create();
-  src->Add(DataConsumerCommand(DataConsumerCommand::kData, "hello"));
-  src->Add(DataConsumerCommand(DataConsumerCommand::kDone));
-
-  EXPECT_EQ(PublicState::kReadableOrWaiting, consumer->GetPublicState());
-  EXPECT_FALSE(loader->IsStarted());
-
-  const char* buffer = nullptr;
-  size_t available;
-  EXPECT_EQ(Result::kShouldWait, consumer->BeginRead(&buffer, &available));
-  EXPECT_EQ(PublicState::kReadableOrWaiting, consumer->GetPublicState());
-  EXPECT_TRUE(loader->IsStarted());
-  EXPECT_EQ(0, client->NumOnStateChangeCalled());
-
-  consumer->DidReceiveResponse(0, ResourceResponse(), std::move(src));
-  EXPECT_EQ(1, client->NumOnStateChangeCalled());
-  test::RunPendingTasks();
-  EXPECT_EQ(2, client->NumOnStateChangeCalled());
-
-  consumer->DidFinishLoading(0);
-  test::RunPendingTasks();
-  EXPECT_EQ(2, client->NumOnStateChangeCalled());
-
-  EXPECT_EQ(PublicState::kReadableOrWaiting, consumer->GetPublicState());
-  ASSERT_EQ(Result::kOk, consumer->BeginRead(&buffer, &available));
-  ASSERT_EQ(5u, available);
-  EXPECT_EQ("hello", String(buffer, available));
-  ASSERT_EQ(Result::kOk, consumer->EndRead(available));
-
-  EXPECT_EQ(PublicState::kReadableOrWaiting, consumer->GetPublicState());
-  EXPECT_EQ(Result::kDone, consumer->BeginRead(&buffer, &available));
-  EXPECT_EQ(2, client->NumOnStateChangeCalled());
+  EXPECT_TRUE(DidStartLoading());
 }
 
 TEST_F(BlobBytesConsumerTest, DrainAsBlobDataHandle) {
-  scoped_refptr<BlobDataHandle> blob_data_handle =
-      BlobDataHandle::Create(BlobData::Create(), 12345);
-  TestThreadableLoader* loader = new TestThreadableLoader();
-  BlobBytesConsumer* consumer = BlobBytesConsumer::CreateForTesting(
-      &GetDocument(), blob_data_handle, loader);
+  String body = "hello, world";
+  scoped_refptr<BlobDataHandle> blob_data_handle = CreateBlob(body);
+  BlobBytesConsumer* consumer =
+      new BlobBytesConsumer(&GetDocument(), blob_data_handle);
 
   EXPECT_EQ(PublicState::kReadableOrWaiting, consumer->GetPublicState());
-  EXPECT_FALSE(loader->IsStarted());
+  EXPECT_FALSE(DidStartLoading());
 
   scoped_refptr<BlobDataHandle> result = consumer->DrainAsBlobDataHandle(
       BytesConsumer::BlobSizePolicy::kDisallowBlobWithInvalidSize);
   ASSERT_TRUE(result);
   EXPECT_FALSE(consumer->DrainAsBlobDataHandle(
       BytesConsumer::BlobSizePolicy::kDisallowBlobWithInvalidSize));
-  EXPECT_EQ(12345u, result->size());
+  EXPECT_EQ(body.length(), result->size());
   EXPECT_EQ(PublicState::kClosed, consumer->GetPublicState());
-  EXPECT_FALSE(loader->IsStarted());
+  EXPECT_FALSE(DidStartLoading());
 }
 
 TEST_F(BlobBytesConsumerTest, DrainAsBlobDataHandle_2) {
-  scoped_refptr<BlobDataHandle> blob_data_handle =
-      BlobDataHandle::Create(BlobData::Create(), -1);
-  TestThreadableLoader* loader = new TestThreadableLoader();
-  BlobBytesConsumer* consumer = BlobBytesConsumer::CreateForTesting(
-      &GetDocument(), blob_data_handle, loader);
+  scoped_refptr<BlobDataHandle> blob_data_handle = BlobDataHandle::Create(
+      "uuid", "", -1, CreateBlob("foo bar")->CloneBlobPtr().PassInterface());
+  ;
+  BlobBytesConsumer* consumer =
+      new BlobBytesConsumer(&GetDocument(), blob_data_handle);
 
   EXPECT_EQ(PublicState::kReadableOrWaiting, consumer->GetPublicState());
-  EXPECT_FALSE(loader->IsStarted());
+  EXPECT_FALSE(DidStartLoading());
 
   scoped_refptr<BlobDataHandle> result = consumer->DrainAsBlobDataHandle(
       BytesConsumer::BlobSizePolicy::kAllowBlobWithInvalidSize);
@@ -405,109 +161,44 @@
       BytesConsumer::BlobSizePolicy::kAllowBlobWithInvalidSize));
   EXPECT_EQ(UINT64_MAX, result->size());
   EXPECT_EQ(PublicState::kClosed, consumer->GetPublicState());
-  EXPECT_FALSE(loader->IsStarted());
+  EXPECT_FALSE(DidStartLoading());
 }
 
 TEST_F(BlobBytesConsumerTest, DrainAsBlobDataHandle_3) {
-  scoped_refptr<BlobDataHandle> blob_data_handle =
-      BlobDataHandle::Create(BlobData::Create(), -1);
-  TestThreadableLoader* loader = new TestThreadableLoader();
-  BlobBytesConsumer* consumer = BlobBytesConsumer::CreateForTesting(
-      &GetDocument(), blob_data_handle, loader);
+  scoped_refptr<BlobDataHandle> blob_data_handle = BlobDataHandle::Create(
+      "uuid", "", -1, CreateBlob("foo bar")->CloneBlobPtr().PassInterface());
+  ;
+  BlobBytesConsumer* consumer =
+      new BlobBytesConsumer(&GetDocument(), blob_data_handle);
 
   EXPECT_EQ(PublicState::kReadableOrWaiting, consumer->GetPublicState());
-  EXPECT_FALSE(loader->IsStarted());
+  EXPECT_FALSE(DidStartLoading());
 
   EXPECT_FALSE(consumer->DrainAsBlobDataHandle(
       BytesConsumer::BlobSizePolicy::kDisallowBlobWithInvalidSize));
   EXPECT_EQ(PublicState::kReadableOrWaiting, consumer->GetPublicState());
-  EXPECT_FALSE(loader->IsStarted());
+  EXPECT_FALSE(DidStartLoading());
 }
 
 TEST_F(BlobBytesConsumerTest, DrainAsFormData) {
-  scoped_refptr<BlobDataHandle> blob_data_handle =
-      BlobDataHandle::Create(BlobData::Create(), 12345);
-  TestThreadableLoader* loader = new TestThreadableLoader();
-  BlobBytesConsumer* consumer = BlobBytesConsumer::CreateForTesting(
-      &GetDocument(), blob_data_handle, loader);
+  String body = "hello, world";
+  scoped_refptr<BlobDataHandle> blob_data_handle = CreateBlob(body);
+  BlobBytesConsumer* consumer =
+      new BlobBytesConsumer(&GetDocument(), blob_data_handle);
 
   EXPECT_EQ(PublicState::kReadableOrWaiting, consumer->GetPublicState());
-  EXPECT_FALSE(loader->IsStarted());
+  EXPECT_FALSE(DidStartLoading());
 
   scoped_refptr<EncodedFormData> result = consumer->DrainAsFormData();
   ASSERT_TRUE(result);
   ASSERT_EQ(1u, result->Elements().size());
   ASSERT_EQ(FormDataElement::kEncodedBlob, result->Elements()[0].type_);
   ASSERT_TRUE(result->Elements()[0].optional_blob_data_handle_);
-  EXPECT_EQ(12345u, result->Elements()[0].optional_blob_data_handle_->size());
+  EXPECT_EQ(body.length(),
+            result->Elements()[0].optional_blob_data_handle_->size());
   EXPECT_EQ(blob_data_handle->Uuid(), result->Elements()[0].blob_uuid_);
   EXPECT_EQ(PublicState::kClosed, consumer->GetPublicState());
-  EXPECT_FALSE(loader->IsStarted());
-}
-
-TEST_F(BlobBytesConsumerTest, LoaderShouldBeCancelled) {
-  {
-    scoped_refptr<BlobDataHandle> blob_data_handle =
-        BlobDataHandle::Create(BlobData::Create(), 12345);
-    TestThreadableLoader* loader = new TestThreadableLoader();
-    BlobBytesConsumer* consumer = BlobBytesConsumer::CreateForTesting(
-        &GetDocument(), blob_data_handle, loader);
-
-    const char* buffer = nullptr;
-    size_t available;
-    EXPECT_EQ(Result::kShouldWait, consumer->BeginRead(&buffer, &available));
-    EXPECT_TRUE(loader->IsStarted());
-    loader->SetShouldBeCancelled();
-  }
-  ThreadState::Current()->CollectAllGarbage();
-}
-
-TEST_F(BlobBytesConsumerTest, SyncErrorDispatch) {
-  scoped_refptr<BlobDataHandle> blob_data_handle =
-      BlobDataHandle::Create(BlobData::Create(), 12345);
-  SyncErrorTestThreadableLoader* loader = new SyncErrorTestThreadableLoader();
-  BlobBytesConsumer* consumer = BlobBytesConsumer::CreateForTesting(
-      &GetDocument(), blob_data_handle, loader);
-  loader->SetClient(consumer);
-  BlobBytesConsumerTestClient* client = new BlobBytesConsumerTestClient();
-  consumer->SetClient(client);
-
-  const char* buffer = nullptr;
-  size_t available;
-  EXPECT_EQ(Result::kError, consumer->BeginRead(&buffer, &available));
-  EXPECT_TRUE(loader->IsStarted());
-
-  EXPECT_EQ(0, client->NumOnStateChangeCalled());
-  EXPECT_EQ(BytesConsumer::PublicState::kErrored, consumer->GetPublicState());
-}
-
-TEST_F(BlobBytesConsumerTest, SyncLoading) {
-  scoped_refptr<BlobDataHandle> blob_data_handle =
-      BlobDataHandle::Create(BlobData::Create(), 12345);
-  SyncLoadingTestThreadableLoader* loader =
-      new SyncLoadingTestThreadableLoader();
-  BlobBytesConsumer* consumer = BlobBytesConsumer::CreateForTesting(
-      &GetDocument(), blob_data_handle, loader);
-  std::unique_ptr<ReplayingHandle> src = ReplayingHandle::Create();
-  src->Add(DataConsumerCommand(DataConsumerCommand::kData, "hello, "));
-  src->Add(DataConsumerCommand(DataConsumerCommand::kWait));
-  src->Add(DataConsumerCommand(DataConsumerCommand::kData, "world"));
-  src->Add(DataConsumerCommand(DataConsumerCommand::kDone));
-  loader->SetClient(consumer);
-  loader->SetHandle(std::move(src));
-  BlobBytesConsumerTestClient* client = new BlobBytesConsumerTestClient();
-  consumer->SetClient(client);
-
-  const char* buffer = nullptr;
-  size_t available;
-  ASSERT_EQ(Result::kOk, consumer->BeginRead(&buffer, &available));
-  EXPECT_TRUE(loader->IsStarted());
-  ASSERT_EQ(7u, available);
-  EXPECT_EQ("hello, ", String(buffer, available));
-
-  EXPECT_EQ(0, client->NumOnStateChangeCalled());
-  EXPECT_EQ(BytesConsumer::PublicState::kReadableOrWaiting,
-            consumer->GetPublicState());
+  EXPECT_FALSE(DidStartLoading());
 }
 
 TEST_F(BlobBytesConsumerTest, ConstructedFromNullHandle) {
diff --git a/third_party/blink/renderer/core/fetch/form_data_bytes_consumer.cc b/third_party/blink/renderer/core/fetch/form_data_bytes_consumer.cc
index 5b457b5..78783242 100644
--- a/third_party/blink/renderer/core/fetch/form_data_bytes_consumer.cc
+++ b/third_party/blink/renderer/core/fetch/form_data_bytes_consumer.cc
@@ -5,6 +5,7 @@
 #include "third_party/blink/renderer/core/fetch/form_data_bytes_consumer.h"
 
 #include "third_party/blink/public/platform/platform.h"
+#include "third_party/blink/renderer/core/execution_context/execution_context.h"
 #include "third_party/blink/renderer/core/fetch/blob_bytes_consumer.h"
 #include "third_party/blink/renderer/core/fetch/bytes_consumer_for_data_consumer_handle.h"
 #include "third_party/blink/renderer/core/typed_arrays/dom_array_buffer.h"
@@ -319,7 +320,6 @@
       case PublicState::kErrored:
         return;
       case PublicState::kClosed:
-        NOTREACHED();
         return;
       case PublicState::kReadableOrWaiting:
         break;
diff --git a/third_party/blink/renderer/core/frame/local_dom_window.cc b/third_party/blink/renderer/core/frame/local_dom_window.cc
index 53a017d..2eceaf8 100644
--- a/third_party/blink/renderer/core/frame/local_dom_window.cc
+++ b/third_party/blink/renderer/core/frame/local_dom_window.cc
@@ -1139,14 +1139,10 @@
                             y * GetFrame()->PageZoomFactor());
   FloatPoint new_scaled_position =
       viewport->ScrollOffsetToPosition(scaled_delta + current_offset);
-  if (RuntimeEnabledFeatures::CSSScrollSnapPointsEnabled()) {
-    new_scaled_position =
-        document()
-            ->GetSnapCoordinator()
-            ->GetSnapPositionForPoint(
-                *document()->GetLayoutView(), new_scaled_position,
-                scroll_to_options.hasLeft(), scroll_to_options.hasTop())
-            .value_or(new_scaled_position);
+  if (SnapCoordinator* coordinator = document()->GetSnapCoordinator()) {
+    new_scaled_position = coordinator->GetSnapPositionForPoint(
+        *document()->GetLayoutView(), new_scaled_position,
+        scroll_to_options.hasLeft(), scroll_to_options.hasTop());
   }
 
   ScrollBehavior scroll_behavior = kScrollBehaviorAuto;
@@ -1214,14 +1210,10 @@
 
   FloatPoint new_scaled_position =
       viewport->ScrollOffsetToPosition(ScrollOffset(scaled_x, scaled_y));
-  if (RuntimeEnabledFeatures::CSSScrollSnapPointsEnabled()) {
-    new_scaled_position =
-        document()
-            ->GetSnapCoordinator()
-            ->GetSnapPositionForPoint(
-                *document()->GetLayoutView(), new_scaled_position,
-                scroll_to_options.hasLeft(), scroll_to_options.hasTop())
-            .value_or(new_scaled_position);
+  if (SnapCoordinator* coordinator = document()->GetSnapCoordinator()) {
+    new_scaled_position = coordinator->GetSnapPositionForPoint(
+        *document()->GetLayoutView(), new_scaled_position,
+        scroll_to_options.hasLeft(), scroll_to_options.hasTop());
   }
 
   ScrollBehavior scroll_behavior = kScrollBehaviorAuto;
diff --git a/third_party/blink/renderer/core/frame/local_frame.cc b/third_party/blink/renderer/core/frame/local_frame.cc
index cbf67de..9fcb0a8 100644
--- a/third_party/blink/renderer/core/frame/local_frame.cc
+++ b/third_party/blink/renderer/core/frame/local_frame.cc
@@ -808,7 +808,8 @@
 
 PositionWithAffinity LocalFrame::PositionForPoint(
     const LayoutPoint& frame_point) {
-  HitTestResult result = GetEventHandler().HitTestResultAtPoint(frame_point);
+  HitTestLocation location(frame_point);
+  HitTestResult result = GetEventHandler().HitTestResultAtLocation(location);
   Node* node = result.InnerNodeOrImageMapImage();
   if (!node)
     return PositionWithAffinity();
@@ -826,12 +827,12 @@
   if (!View())
     return nullptr;
 
-  LayoutPoint pt = View()->ConvertFromRootFrame(point_in_root_frame);
+  HitTestLocation location(View()->ConvertFromRootFrame(point_in_root_frame));
 
   if (!ContentLayoutObject())
     return nullptr;
-  HitTestResult result = GetEventHandler().HitTestResultAtPoint(
-      pt, HitTestRequest::kReadOnly | HitTestRequest::kActive);
+  HitTestResult result = GetEventHandler().HitTestResultAtLocation(
+      location, HitTestRequest::kReadOnly | HitTestRequest::kActive);
   return result.InnerNode() ? &result.InnerNode()->GetDocument() : nullptr;
 }
 
@@ -1430,8 +1431,4 @@
   pause_handle_bindings_.CloseAllBindings();
 }
 
-void LocalFrame::AnimateSnapFling(base::TimeTicks monotonic_time) {
-  GetEventHandler().AnimateSnapFling(monotonic_time);
-}
-
 }  // namespace blink
diff --git a/third_party/blink/renderer/core/frame/local_frame.h b/third_party/blink/renderer/core/frame/local_frame.h
index 178e41fe..6fad623 100644
--- a/third_party/blink/renderer/core/frame/local_frame.h
+++ b/third_party/blink/renderer/core/frame/local_frame.h
@@ -359,8 +359,6 @@
 
   void ResumeSubresourceLoading();
 
-  void AnimateSnapFling(base::TimeTicks monotonic_time);
-
  private:
   friend class FrameNavigationDisabler;
 
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 e1f2f96f..1c8299d 100644
--- a/third_party/blink/renderer/core/frame/local_frame_view.cc
+++ b/third_party/blink/renderer/core/frame/local_frame_view.cc
@@ -2530,9 +2530,8 @@
       // See crbug.com/667547
       bool print_mode_enabled = frame_->GetDocument()->Printing() &&
                                 !RuntimeEnabledFeatures::PrintBrowserEnabled();
-      bool repainted_paint_controller = false;
       if (!print_mode_enabled)
-        repainted_paint_controller = PaintTree();
+        PaintTree();
 
       if (!RuntimeEnabledFeatures::SlimmingPaintV2Enabled()) {
         if (layout_view->Compositor()->InCompositingMode()) {
@@ -2553,8 +2552,7 @@
 
           // Notify the controller that the artifact has been pushed and some
           // lifecycle state can be freed (such as raster invalidations).
-          if (repainted_paint_controller)
-            paint_controller_->FinishCycle();
+          paint_controller_->FinishCycle();
           // PaintController for BlinkGenPropertyTrees is transient.
           if (RuntimeEnabledFeatures::BlinkGenPropertyTreesEnabled())
             paint_controller_ = nullptr;
@@ -2725,7 +2723,7 @@
   CollectDrawableLayersForLayerListRecursively(context, layer->MaskLayer());
 }
 
-bool LocalFrameView::PaintTree() {
+void LocalFrameView::PaintTree() {
   TRACE_EVENT0("blink,benchmark", "LocalFrameView::paintTree");
   SCOPED_UMA_AND_UKM_TIMER("Blink.Paint.UpdateTime", UkmMetricNames::kPaint);
 
@@ -2739,7 +2737,6 @@
     frame_view.Lifecycle().AdvanceTo(DocumentLifecycle::kInPaint);
   });
 
-  bool repainted_paint_controller = false;
   if (RuntimeEnabledFeatures::SlimmingPaintV2Enabled()) {
     if (!paint_controller_)
       paint_controller_ = PaintController::Create();
@@ -2762,7 +2759,6 @@
       PaintInternal(graphics_context, kGlobalPaintNormalPhase,
                     CullRect(LayoutRect::InfiniteIntRect()));
       paint_controller_->CommitNewDisplayItems();
-      repainted_paint_controller = true;
     }
   } else {
     // A null graphics layer can occur for painting of SVG images that are not
@@ -2813,7 +2809,6 @@
     CollectDrawableLayersForLayerListRecursively(
         context, layout_view->Compositor()->PaintRootGraphicsLayer());
     paint_controller_->CommitNewDisplayItems();
-    repainted_paint_controller = true;
   }
 
   ForAllNonThrottledLocalFrameViews([](LocalFrameView& frame_view) {
@@ -2821,8 +2816,6 @@
     if (auto* layout_view = frame_view.GetLayoutView())
       layout_view->Layer()->ClearNeedsRepaintRecursively();
   });
-
-  return repainted_paint_controller;
 }
 
 void LocalFrameView::PushPaintArtifactToCompositor(
diff --git a/third_party/blink/renderer/core/frame/local_frame_view.h b/third_party/blink/renderer/core/frame/local_frame_view.h
index e77966e..247526a 100644
--- a/third_party/blink/renderer/core/frame/local_frame_view.h
+++ b/third_party/blink/renderer/core/frame/local_frame_view.h
@@ -664,10 +664,7 @@
   void NotifyFrameRectsChangedIfNeededRecursive();
   void UpdateStyleAndLayoutIfNeededRecursive();
   void PrePaint();
-  // Returns true if paint_controller_ is repainted. The return value is not
-  // used for non-BlinkGenPropertyTrees/SPv2 where paint_controller_ doesn't
-  // exist.
-  bool PaintTree();
+  void PaintTree();
 
   void UpdateStyleAndLayoutIfNeededRecursiveInternal();
 
diff --git a/third_party/blink/renderer/core/frame/rotation_viewport_anchor.cc b/third_party/blink/renderer/core/frame/rotation_viewport_anchor.cc
index 2f07827..95d9c04 100644
--- a/third_party/blink/renderer/core/frame/rotation_viewport_anchor.cc
+++ b/third_party/blink/renderer/core/frame/rotation_viewport_anchor.cc
@@ -27,10 +27,12 @@
                              const IntRect& view_rect,
                              EventHandler& event_handler) {
   IntPoint point = FlooredIntPoint(absolute_point);
-  Node* node = event_handler
-                   .HitTestResultAtPoint(point, HitTestRequest::kReadOnly |
-                                                    HitTestRequest::kActive)
-                   .InnerNode();
+  HitTestLocation location(point);
+  Node* node =
+      event_handler
+          .HitTestResultAtLocation(
+              location, HitTestRequest::kReadOnly | HitTestRequest::kActive)
+          .InnerNode();
 
   if (!node)
     return nullptr;
@@ -47,10 +49,10 @@
   if (node_size.Width() * node_size.Height() > max_node_area) {
     IntSize point_offset = view_rect.Size();
     point_offset.Scale(kViewportAnchorRelativeEpsilon);
+    HitTestLocation location(point + point_offset);
     node = event_handler
-               .HitTestResultAtPoint(
-                   point + point_offset,
-                   HitTestRequest::kReadOnly | HitTestRequest::kActive)
+               .HitTestResultAtLocation(location, HitTestRequest::kReadOnly |
+                                                      HitTestRequest::kActive)
                .InnerNode();
   }
 
diff --git a/third_party/blink/renderer/core/frame/use_counter_test.cc b/third_party/blink/renderer/core/frame/use_counter_test.cc
index 062580f..78de92d 100644
--- a/third_party/blink/renderer/core/frame/use_counter_test.cc
+++ b/third_party/blink/renderer/core/frame/use_counter_test.cc
@@ -283,6 +283,34 @@
   EXPECT_FALSE(UseCounter::IsCounted(document, feature));
 }
 
+TEST_F(UseCounterTest, CSSGridLayoutPercentageColumnIndefiniteWidth) {
+  std::unique_ptr<DummyPageHolder> dummy_page_holder =
+      DummyPageHolder::Create(IntSize(800, 600));
+  Page::InsertOrdinaryPageForTesting(&dummy_page_holder->GetPage());
+  Document& document = dummy_page_holder->GetDocument();
+  WebFeature feature = WebFeature::kGridRowTrackPercentIndefiniteHeight;
+  EXPECT_FALSE(UseCounter::IsCounted(document, feature));
+  document.documentElement()->SetInnerHTMLFromString(
+      "<div style='display: inline-grid; grid-template-columns: 50%;'>"
+      "</div>");
+  document.View()->UpdateAllLifecyclePhases();
+  EXPECT_FALSE(UseCounter::IsCounted(document, feature));
+}
+
+TEST_F(UseCounterTest, CSSGridLayoutPercentageRowIndefiniteHeight) {
+  std::unique_ptr<DummyPageHolder> dummy_page_holder =
+      DummyPageHolder::Create(IntSize(800, 600));
+  Page::InsertOrdinaryPageForTesting(&dummy_page_holder->GetPage());
+  Document& document = dummy_page_holder->GetDocument();
+  WebFeature feature = WebFeature::kGridRowTrackPercentIndefiniteHeight;
+  EXPECT_FALSE(UseCounter::IsCounted(document, feature));
+  document.documentElement()->SetInnerHTMLFromString(
+      "<div style='display: inline-grid; grid-template-rows: 50%;'>"
+      "</div>");
+  document.View()->UpdateAllLifecyclePhases();
+  EXPECT_TRUE(UseCounter::IsCounted(document, feature));
+}
+
 class DeprecationTest : public testing::Test {
  public:
   DeprecationTest()
diff --git a/third_party/blink/renderer/core/frame/web_frame_widget_impl.cc b/third_party/blink/renderer/core/frame/web_frame_widget_impl.cc
index 5f075f3..54cf415 100644
--- a/third_party/blink/renderer/core/frame/web_frame_widget_impl.cc
+++ b/third_party/blink/renderer/core/frame/web_frame_widget_impl.cc
@@ -91,8 +91,7 @@
 
 // Returns a rect which is offset and scaled accordingly to |base_rect|'s
 // location and size.
-FloatRect NormalizeRect(const FloatRect& to_normalize,
-                        const FloatRect& base_rect) {
+FloatRect NormalizeRect(const IntRect& to_normalize, const IntRect& base_rect) {
   FloatRect result(to_normalize);
   result.SetLocation(to_normalize.Location() + (-base_rect.Location()));
   result.Scale(1.0 / base_rect.Width(), 1.0 / base_rect.Height());
@@ -751,10 +750,11 @@
   // capture because it will interfere with the scrollbar receiving events.
   LayoutPoint point(event.PositionInWidget().x, event.PositionInWidget().y);
   if (event.button == WebMouseEvent::Button::kLeft) {
-    point = LocalRootImpl()->GetFrameView()->ConvertFromRootFrame(point);
+    HitTestLocation location(
+        LocalRootImpl()->GetFrameView()->ConvertFromRootFrame(point));
     HitTestResult result(
-        LocalRootImpl()->GetFrame()->GetEventHandler().HitTestResultAtPoint(
-            point));
+        LocalRootImpl()->GetFrame()->GetEventHandler().HitTestResultAtLocation(
+            location));
     result.SetToShadowHostIfInRestrictedShadowRoot();
     Node* hit_node = result.InnerNode();
 
@@ -1142,9 +1142,10 @@
   LayoutPoint doc_point(
       LocalRootImpl()->GetFrame()->View()->ConvertFromRootFrame(
           pos_in_root_frame));
+  HitTestLocation location(doc_point);
   HitTestResult result =
-      LocalRootImpl()->GetFrame()->GetEventHandler().HitTestResultAtPoint(
-          doc_point, HitTestRequest::kReadOnly | HitTestRequest::kActive);
+      LocalRootImpl()->GetFrame()->GetEventHandler().HitTestResultAtLocation(
+          location, HitTestRequest::kReadOnly | HitTestRequest::kActive);
   result.SetToShadowHostIfInRestrictedShadowRoot();
   return result;
 }
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 1b06b93..f8746d77 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
@@ -299,7 +299,7 @@
 
     // The page rect gets scaled and translated, so specify the entire
     // print content area here as the recording rect.
-    IntRect bounds(0, 0, printed_page_height_, printed_page_width_);
+    FloatRect bounds(0, 0, printed_page_height_, printed_page_width_);
     PaintRecordBuilder builder(&canvas->getMetaData());
     builder.Context().SetPrinting(true);
     builder.Context().BeginRecording(bounds);
@@ -326,7 +326,7 @@
     const float page_width = page_size_in_pixels.Width();
     size_t num_pages = PageRects().size();
     int total_height = num_pages * (page_size_in_pixels.Height() + 1) - 1;
-    IntRect all_pages_rect(0, 0, page_width, total_height);
+    FloatRect all_pages_rect(0, 0, page_width, total_height);
 
     PaintRecordBuilder builder(&canvas->getMetaData());
     GraphicsContext& context = builder.Context();
@@ -1046,9 +1046,10 @@
   if (!GetFrame())
     return kNotFound;
 
-  LayoutPoint point = GetFrame()->View()->ViewportToFrame(point_in_viewport);
-  HitTestResult result = GetFrame()->GetEventHandler().HitTestResultAtPoint(
-      point, HitTestRequest::kReadOnly | HitTestRequest::kActive);
+  HitTestLocation location(
+      GetFrame()->View()->ViewportToFrame(point_in_viewport));
+  HitTestResult result = GetFrame()->GetEventHandler().HitTestResultAtLocation(
+      location, HitTestRequest::kReadOnly | HitTestRequest::kActive);
   return GetFrame()->Selection().CharacterIndexForPoint(
       result.RoundedPointInInnerNodeFrame());
 }
@@ -1966,10 +1967,10 @@
   IntPoint root_frame_point(
       GetFrame()->GetPage()->GetVisualViewport().ViewportToRootFrame(
           pos_in_viewport));
-  IntPoint doc_point(
+  HitTestLocation location(
       GetFrame()->View()->ConvertFromRootFrame(root_frame_point));
-  HitTestResult result = GetFrame()->GetEventHandler().HitTestResultAtPoint(
-      doc_point, HitTestRequest::kReadOnly | HitTestRequest::kActive);
+  HitTestResult result = GetFrame()->GetEventHandler().HitTestResultAtLocation(
+      location, HitTestRequest::kReadOnly | HitTestRequest::kActive);
   result.SetToShadowHostIfInRestrictedShadowRoot();
   return result;
 }
diff --git a/third_party/blink/renderer/core/html/canvas/canvas_async_blob_creator.cc b/third_party/blink/renderer/core/html/canvas/canvas_async_blob_creator.cc
index dcc0b23..d33fc83 100644
--- a/third_party/blink/renderer/core/html/canvas/canvas_async_blob_creator.cc
+++ b/third_party/blink/renderer/core/html/canvas/canvas_async_blob_creator.cc
@@ -251,7 +251,7 @@
       }
       DCHECK(skia_image->colorSpace());
       skia_image = skia_image->makeColorSpace(
-          blob_color_space, SkTransferFunctionBehavior::kRespect);
+          blob_color_space, SkTransferFunctionBehavior::kIgnore);
       image_ = StaticBitmapImage::Create(skia_image);
     }
 
@@ -335,7 +335,7 @@
   SkTransferFunctionBehavior transfer_fn_behavior =
       SkTransferFunctionBehavior::kIgnore;
   if (function_type_ == kHTMLCanvasConvertToBlobPromise)
-    transfer_fn_behavior = SkTransferFunctionBehavior::kRespect;
+    transfer_fn_behavior = SkTransferFunctionBehavior::kIgnore;
   return buffer->EncodeImage("image/webp", quality, &encoded_image_,
                              transfer_fn_behavior);
 }
@@ -658,7 +658,7 @@
     return false;
   return buffer->EncodeImage(encode_options_.type(), encode_options_.quality(),
                              &encoded_image_,
-                             SkTransferFunctionBehavior::kRespect);
+                             SkTransferFunctionBehavior::kIgnore);
 }
 
 }  // namespace blink
diff --git a/third_party/blink/renderer/core/html/canvas/canvas_async_blob_creator_test.cc b/third_party/blink/renderer/core/html/canvas/canvas_async_blob_creator_test.cc
index e196404..252f607 100644
--- a/third_party/blink/renderer/core/html/canvas/canvas_async_blob_creator_test.cc
+++ b/third_party/blink/renderer/core/html/canvas/canvas_async_blob_creator_test.cc
@@ -302,7 +302,7 @@
           sk_sp<SkImage> ref_image = source_image->makeColorSpace(
               CanvasAsyncBlobCreator::BlobColorSpaceToSkColorSpace(
                   blob_color_space),
-              SkTransferFunctionBehavior::kRespect);
+              SkTransferFunctionBehavior::kIgnore);
 
           // Jpeg does not support transparent images.
           bool compare_alpha = (blob_mime_type != "image/jpeg");
diff --git a/third_party/blink/renderer/core/imagebitmap/image_bitmap.cc b/third_party/blink/renderer/core/imagebitmap/image_bitmap.cc
index 9e8de0a..4f1cd8d 100644
--- a/third_party/blink/renderer/core/imagebitmap/image_bitmap.cc
+++ b/third_party/blink/renderer/core/imagebitmap/image_bitmap.cc
@@ -448,7 +448,7 @@
     scoped_refptr<StaticBitmapImage>&& image,
     ImageBitmap::ParsedOptions& options) {
   SkTransferFunctionBehavior transfer_function_behavior =
-      SkTransferFunctionBehavior::kRespect;
+      SkTransferFunctionBehavior::kIgnore;
   // We normally expect to respect transfer function. However, in two scenarios
   // we have to ignore the transfer function. First, when the source image is
   // unpremul. Second, when the source image is drawn using a
diff --git a/third_party/blink/renderer/core/input/event_handler.cc b/third_party/blink/renderer/core/input/event_handler.cc
index ee85ab0..bd07a2ac 100644
--- a/third_party/blink/renderer/core/input/event_handler.cc
+++ b/third_party/blink/renderer/core/input/event_handler.cc
@@ -233,7 +233,8 @@
   mouse_event_manager_->InvalidateClick();
 }
 
-void EventHandler::PerformHitTest(HitTestResult& result,
+void EventHandler::PerformHitTest(const HitTestLocation& location,
+                                  HitTestResult& result,
                                   bool no_lifecycle_update) const {
   // LayoutView::hitTest causes a layout, and we don't want to hit that until
   // the first layout because until then, there is nothing shown on the screen -
@@ -251,9 +252,9 @@
     return;
 
   if (no_lifecycle_update) {
-    frame_->ContentLayoutObject()->HitTestNoLifecycleUpdate(result);
+    frame_->ContentLayoutObject()->HitTestNoLifecycleUpdate(location, result);
   } else {
-    frame_->ContentLayoutObject()->HitTest(result);
+    frame_->ContentLayoutObject()->HitTest(location, result);
   }
   const HitTestRequest& request = result.GetHitTestRequest();
   if (!request.ReadOnly()) {
@@ -262,14 +263,14 @@
   }
 }
 
-HitTestResult EventHandler::HitTestResultAtPoint(
-    const LayoutPoint& point,
+HitTestResult EventHandler::HitTestResultAtLocation(
+    const HitTestLocation& location,
     HitTestRequest::HitTestRequestType hit_type,
     const LayoutObject* stop_node,
     bool no_lifecycle_update) {
-  TRACE_EVENT0("blink", "EventHandler::hitTestResultAtPoint");
+  TRACE_EVENT0("blink", "EventHandler::HitTestResultAtLocation");
 
-  // We always send hitTestResultAtPoint to the main frame if we have one,
+  // We always send HitTestResultAtLocation to the main frame if we have one,
   // otherwise we might hit areas that are obscured by higher frames.
   if (frame_->GetPage()) {
     LocalFrame& main_frame = frame_->LocalFrameRoot();
@@ -277,53 +278,30 @@
       LocalFrameView* frame_view = frame_->View();
       LocalFrameView* main_view = main_frame.View();
       if (frame_view && main_view) {
-        LayoutPoint main_content_point = main_view->ConvertFromRootFrame(
-            frame_view->ConvertToRootFrame(point));
-        return main_frame.GetEventHandler().HitTestResultAtPoint(
-            main_content_point, hit_type, stop_node);
+        if (location.IsRectBasedTest()) {
+          DCHECK(location.IsRectilinear());
+          LayoutPoint main_content_point =
+              main_view->ConvertFromRootFrame(frame_view->ConvertToRootFrame(
+                  location.BoundingBox().Location()));
+          HitTestLocation adjusted_location(
+              (LayoutRect(main_content_point, location.BoundingBox().Size())));
+          return main_frame.GetEventHandler().HitTestResultAtLocation(
+              adjusted_location, hit_type, stop_node);
+        } else {
+          HitTestLocation adjusted_location(main_view->ConvertFromRootFrame(
+              frame_view->ConvertToRootFrame(location.Point())));
+          return main_frame.GetEventHandler().HitTestResultAtLocation(
+              adjusted_location, hit_type, stop_node);
+        }
       }
     }
   }
-
-  // hitTestResultAtPoint is specifically used to hitTest into all frames, thus
-  // it always allows child frame content.
+  // HitTestResultAtLocation is specifically used to hitTest into all frames,
+  // thus it always allows child frame content.
   HitTestRequest request(hit_type | HitTestRequest::kAllowChildFrameContent,
                          stop_node);
-  HitTestResult result(request, point);
-  PerformHitTest(result, no_lifecycle_update);
-  return result;
-}
-
-HitTestResult EventHandler::HitTestResultAtRect(
-    const LayoutRect& rect,
-    HitTestRequest::HitTestRequestType hit_type,
-    const LayoutObject* stop_node,
-    bool no_lifecycle_update) {
-  TRACE_EVENT0("blink", "EventHandler::hitTestResultAtRect");
-
-  DCHECK(hit_type & HitTestRequest::kListBased);
-  DCHECK(!rect.IsEmpty());
-
-  if (frame_->GetPage()) {
-    LocalFrame& main_frame = frame_->LocalFrameRoot();
-    if (frame_ != &main_frame) {
-      LocalFrameView* frame_view = frame_->View();
-      LocalFrameView* main_view = main_frame.View();
-      if (frame_view && main_view) {
-        LayoutPoint main_content_point = main_view->ConvertFromRootFrame(
-            frame_view->ConvertToRootFrame(rect.Location()));
-        return main_frame.GetEventHandler().HitTestResultAtRect(
-            LayoutRect(main_content_point, rect.Size()), hit_type, stop_node);
-      }
-    }
-  }
-
-  // hitTestResultAtPoint is specifically used to hitTest into all frames, thus
-  // it always allows child frame content.
-  HitTestRequest request(hit_type | HitTestRequest::kAllowChildFrameContent,
-                         stop_node);
-  HitTestResult result(request, rect);
-  PerformHitTest(result, no_lifecycle_update);
+  HitTestResult result(request, location);
+  PerformHitTest(location, result, no_lifecycle_update);
   return result;
 }
 
@@ -393,14 +371,14 @@
 
   HitTestRequest request(HitTestRequest::kReadOnly |
                          HitTestRequest::kAllowChildFrameContent);
-  HitTestResult result(
-      request,
+  HitTestLocation location(
       view->ViewportToFrame(mouse_event_manager_->LastKnownMousePosition()));
-  layout_view->HitTest(result);
+  HitTestResult result(request, location);
+  layout_view->HitTest(location, result);
 
   if (LocalFrame* frame = result.InnerNodeFrame()) {
     EventHandler::OptionalCursor optional_cursor =
-        frame->GetEventHandler().SelectCursor(result);
+        frame->GetEventHandler().SelectCursor(location, result);
     if (optional_cursor.IsCursorChange()) {
       view->SetCursor(optional_cursor.GetCursor());
     }
@@ -408,12 +386,12 @@
 }
 
 bool EventHandler::ShouldShowResizeForNode(const Node* node,
-                                           const HitTestResult& result) {
+                                           const HitTestLocation& location) {
   if (LayoutObject* layout_object = node->GetLayoutObject()) {
     PaintLayer* layer = layout_object->EnclosingLayer();
     if (layer->GetScrollableArea() &&
         layer->GetScrollableArea()->IsPointInResizeControl(
-            result.RoundedPointInMainFrame(), kResizerForPointer)) {
+            RoundedIntPoint(location.Point()), kResizerForPointer)) {
       return true;
     }
   }
@@ -447,6 +425,7 @@
 }
 
 EventHandler::OptionalCursor EventHandler::SelectCursor(
+    const HitTestLocation& location,
     const HitTestResult& result) {
   if (scroll_manager_->InResizeMode())
     return kNoCursorChange;
@@ -464,7 +443,7 @@
   if (!node)
     return SelectAutoCursor(result, node, IBeamCursor());
 
-  if (ShouldShowResizeForNode(node, result))
+  if (ShouldShowResizeForNode(node, location))
     return PointerCursor();
 
   LayoutObject* layout_object = node->GetLayoutObject();
@@ -790,23 +769,25 @@
     const WebMouseEvent& event,
     const Vector<WebMouseEvent>& coalesced_events) {
   TRACE_EVENT0("blink", "EventHandler::handleMouseMoveEvent");
-  HitTestResult hovered_node = HitTestResult();
-  WebInputEventResult result =
-      HandleMouseMoveOrLeaveEvent(event, coalesced_events, &hovered_node);
+  HitTestResult hovered_node_result;
+  HitTestLocation location;
+  WebInputEventResult result = HandleMouseMoveOrLeaveEvent(
+      event, coalesced_events, &hovered_node_result, &location);
 
   Page* page = frame_->GetPage();
   if (!page)
     return result;
 
   if (PaintLayer* layer =
-          EventHandlingUtil::LayerForNode(hovered_node.InnerNode())) {
+          EventHandlingUtil::LayerForNode(hovered_node_result.InnerNode())) {
     if (ScrollableArea* layer_scrollable_area =
             EventHandlingUtil::AssociatedScrollableArea(layer))
       layer_scrollable_area->MouseMovedInContentArea();
   }
 
-  hovered_node.SetToShadowHostIfInRestrictedShadowRoot();
-  page->GetChromeClient().MouseDidMoveOverElement(*frame_, hovered_node);
+  hovered_node_result.SetToShadowHostIfInRestrictedShadowRoot();
+  page->GetChromeClient().MouseDidMoveOverElement(*frame_, location,
+                                                  hovered_node_result);
 
   return result;
 }
@@ -817,14 +798,15 @@
   Page* page = frame_->GetPage();
   if (page)
     page->GetChromeClient().ClearToolTip(*frame_);
-  HandleMouseMoveOrLeaveEvent(event, Vector<WebMouseEvent>(), nullptr, false,
-                              true);
+  HandleMouseMoveOrLeaveEvent(event, Vector<WebMouseEvent>(), nullptr, nullptr,
+                              false, true);
 }
 
 WebInputEventResult EventHandler::HandleMouseMoveOrLeaveEvent(
     const WebMouseEvent& mouse_event,
     const Vector<WebMouseEvent>& coalesced_events,
-    HitTestResult* hovered_node,
+    HitTestResult* hovered_node_result,
+    HitTestLocation* hit_test_location,
     bool only_update_scrollbars,
     bool force_leave) {
   DCHECK(frame_);
@@ -892,8 +874,9 @@
   if (pointer_event_manager_->IsAnyTouchActive() && !force_leave)
     hit_type |= HitTestRequest::kActive | HitTestRequest::kReadOnly;
   HitTestRequest request(hit_type);
+  HitTestLocation out_location((LayoutPoint()));
   MouseEventWithHitTestResults mev = MouseEventWithHitTestResults(
-      mouse_event, HitTestResult(request, LayoutPoint()));
+      mouse_event, out_location, HitTestResult(request, out_location));
 
   // We don't want to do a hit-test in forceLeave scenarios because there
   // might actually be some other frame above this one at the specified
@@ -906,8 +889,11 @@
                                                       mouse_event);
   }
 
-  if (hovered_node)
-    *hovered_node = mev.GetHitTestResult();
+  if (hovered_node_result)
+    *hovered_node_result = mev.GetHitTestResult();
+
+  if (hit_test_location)
+    *hit_test_location = mev.GetHitTestLocation();
 
   Scrollbar* scrollbar = nullptr;
 
@@ -951,8 +937,8 @@
     // subframe of the target node to be detached from its LocalFrameView, in
     // which case the event should not be passed.
     if (new_subframe->View()) {
-      event_result = PassMouseMoveEventToSubframe(mev, coalesced_events,
-                                                  new_subframe, hovered_node);
+      event_result = PassMouseMoveEventToSubframe(
+          mev, coalesced_events, new_subframe, hovered_node_result);
     }
   } else {
     if (scrollbar && !mouse_event_manager_->MousePressed()) {
@@ -962,7 +948,7 @@
     }
     if (LocalFrameView* view = frame_->View()) {
       EventHandler::OptionalCursor optional_cursor =
-          SelectCursor(mev.GetHitTestResult());
+          SelectCursor(mev.GetHitTestLocation(), mev.GetHitTestResult());
       if (optional_cursor.IsCursorChange()) {
         view->SetCursor(optional_cursor.GetCursor());
       }
@@ -1229,10 +1215,6 @@
   should_only_fire_drag_over_event_ = false;
 }
 
-void EventHandler::AnimateSnapFling(base::TimeTicks monotonic_time) {
-  scroll_manager_->AnimateSnapFling(monotonic_time);
-}
-
 void EventHandler::SetCapturingMouseEventsNode(Node* n) {
   capturing_mouse_events_node_ = n;
   event_handler_will_reset_capturing_mouse_events_node_ = false;
@@ -1469,6 +1451,7 @@
 }
 
 bool EventHandler::BestClickableNodeForHitTestResult(
+    const HitTestLocation& location,
     const HitTestResult& result,
     IntPoint& target_point,
     Node*& target_node) {
@@ -1487,9 +1470,9 @@
   }
 
   IntPoint touch_center =
-      frame_->View()->ConvertToRootFrame(result.RoundedPointInMainFrame());
-  IntRect touch_rect = frame_->View()->ConvertToRootFrame(
-      result.GetHitTestLocation().EnclosingIntRect());
+      frame_->View()->ConvertToRootFrame(RoundedIntPoint(location.Point()));
+  IntRect touch_rect =
+      frame_->View()->ConvertToRootFrame(location.EnclosingIntRect());
 
   HeapVector<Member<Node>, 11> nodes;
   CopyToVector(result.ListBasedTestResult(), nodes);
@@ -1502,14 +1485,15 @@
 }
 
 bool EventHandler::BestContextMenuNodeForHitTestResult(
+    const HitTestLocation& location,
     const HitTestResult& result,
     IntPoint& target_point,
     Node*& target_node) {
   DCHECK(result.IsRectBasedTest());
   IntPoint touch_center =
-      frame_->View()->ConvertToRootFrame(result.RoundedPointInMainFrame());
-  IntRect touch_rect = frame_->View()->ConvertToRootFrame(
-      result.GetHitTestLocation().EnclosingIntRect());
+      frame_->View()->ConvertToRootFrame(RoundedIntPoint(location.Point()));
+  IntRect touch_rect =
+      frame_->View()->ConvertToRootFrame(location.EnclosingIntRect());
   HeapVector<Member<Node>, 11> nodes;
   CopyToVector(result.ListBasedTestResult(), nodes);
 
@@ -1751,23 +1735,25 @@
     }
   }
 
+  HitTestLocation location;
   LocalFrame& root_frame = frame_->LocalFrameRoot();
   HitTestResult hit_test_result;
   if (hit_rect_size.IsEmpty()) {
-    hit_test_result = root_frame.GetEventHandler().HitTestResultAtPoint(
-        LayoutPoint(adjusted_event.PositionInRootFrame()), hit_type);
+    location = HitTestLocation(adjusted_event.PositionInRootFrame());
+    hit_test_result = root_frame.GetEventHandler().HitTestResultAtLocation(
+        location, hit_type);
   } else {
     LayoutPoint top_left(adjusted_event.PositionInRootFrame());
     top_left.Move(-hit_rect_size * 0.5f);
-    hit_test_result = root_frame.GetEventHandler().HitTestResultAtRect(
-        LayoutRect(top_left, hit_rect_size), hit_type);
+    location = HitTestLocation(LayoutRect(top_left, hit_rect_size));
+    hit_test_result = root_frame.GetEventHandler().HitTestResultAtLocation(
+        location, hit_type);
   }
 
   if (hit_test_result.IsRectBasedTest()) {
     // Adjust the location of the gesture to the most likely nearby node, as
     // appropriate for the type of event.
-    ApplyTouchAdjustment(&adjusted_event, &hit_test_result);
-
+    ApplyTouchAdjustment(&adjusted_event, location, &hit_test_result);
     // Do a new hit-test at the (adjusted) gesture co-ordinates. This is
     // necessary because rect-based hit testing and touch adjustment sometimes
     // return a different node than what a point-based hit test would return for
@@ -1797,10 +1783,12 @@
     UMA_HISTOGRAM_COUNTS_100("Event.Touch.TouchAdjustment.AdjustDistance",
                              static_cast<int>(adjusted_distance));
   }
-  return GestureEventWithHitTestResults(adjusted_event, hit_test_result);
+  return GestureEventWithHitTestResults(adjusted_event, location,
+                                        hit_test_result);
 }
 
 void EventHandler::ApplyTouchAdjustment(WebGestureEvent* gesture_event,
+                                        HitTestLocation& location,
                                         HitTestResult* hit_test_result) {
   Node* adjusted_node = nullptr;
   IntPoint adjusted_point =
@@ -1812,13 +1800,13 @@
     case WebInputEvent::kGestureTapDown:
     case WebInputEvent::kGestureShowPress:
       adjusted = BestClickableNodeForHitTestResult(
-          *hit_test_result, adjusted_point, adjusted_node);
+          location, *hit_test_result, adjusted_point, adjusted_node);
       break;
     case WebInputEvent::kGestureLongPress:
     case WebInputEvent::kGestureLongTap:
     case WebInputEvent::kGestureTwoFingerTap:
       adjusted = BestContextMenuNodeForHitTestResult(
-          *hit_test_result, adjusted_point, adjusted_node);
+          location, *hit_test_result, adjusted_point, adjusted_node);
       break;
     default:
       NOTREACHED();
@@ -1829,8 +1817,9 @@
   // FIXME: We should do this even when no candidate matches the node filter.
   // crbug.com/398914
   if (adjusted) {
-    hit_test_result->ResolveRectBasedTest(
-        adjusted_node, frame_->View()->ConvertFromRootFrame(adjusted_point));
+    LayoutPoint point = frame_->View()->ConvertFromRootFrame(adjusted_point);
+    DCHECK(location.ContainsPoint(FloatPoint(point)));
+    location = hit_test_result->ResolveRectBasedTest(adjusted_node, point);
     gesture_event->ApplyTouchAdjustment(
         WebFloatPoint(adjusted_point.X(), adjusted_point.Y()));
   }
@@ -1944,7 +1933,8 @@
 
   // Use the focused node as the target for hover and active.
   HitTestRequest request(HitTestRequest::kActive);
-  HitTestResult result(request, location_in_root_frame);
+  HitTestLocation location(location_in_root_frame);
+  HitTestResult result(request, location);
   result.SetInnerNode(target_node);
   doc->UpdateHoverActiveState(request, result.InnerElement());
 
@@ -2020,10 +2010,10 @@
   if (auto* layout_object = frame_->ContentLayoutObject()) {
     if (LocalFrameView* view = frame_->View()) {
       HitTestRequest request(HitTestRequest::kMove);
-      HitTestResult result(request,
-                           view->ViewportToFrame(
-                               mouse_event_manager_->LastKnownMousePosition()));
-      layout_object->HitTest(result);
+      HitTestLocation location(view->ViewportToFrame(
+          mouse_event_manager_->LastKnownMousePosition()));
+      HitTestResult result(request, location);
+      layout_object->HitTest(location, result);
       frame_->GetDocument()->UpdateHoverActiveState(request,
                                                     result.InnerElement());
     }
@@ -2178,12 +2168,13 @@
     MouseEventWithHitTestResults& mev,
     const Vector<WebMouseEvent>& coalesced_events,
     LocalFrame* subframe,
-    HitTestResult* hovered_node) {
+    HitTestResult* hovered_node,
+    HitTestLocation* hit_test_location) {
   if (mouse_event_manager_->MouseDownMayStartDrag())
     return WebInputEventResult::kNotHandled;
   WebInputEventResult result =
       subframe->GetEventHandler().HandleMouseMoveOrLeaveEvent(
-          mev.Event(), coalesced_events, hovered_node);
+          mev.Event(), coalesced_events, hovered_node, hit_test_location);
   if (result != WebInputEventResult::kNotHandled)
     return result;
   return WebInputEventResult::kHandledSystem;
diff --git a/third_party/blink/renderer/core/input/event_handler.h b/third_party/blink/renderer/core/input/event_handler.h
index 58a295c..99affe7 100644
--- a/third_party/blink/renderer/core/input/event_handler.h
+++ b/third_party/blink/renderer/core/input/event_handler.h
@@ -99,15 +99,8 @@
   void DispatchFakeMouseMoveEventSoon(MouseEventManager::FakeMouseMoveReason);
   void DispatchFakeMouseMoveEventSoonInQuad(const FloatQuad&);
 
-  HitTestResult HitTestResultAtPoint(
-      const LayoutPoint&,
-      HitTestRequest::HitTestRequestType hit_type = HitTestRequest::kReadOnly |
-                                                    HitTestRequest::kActive,
-      const LayoutObject* stop_node = nullptr,
-      bool no_lifecycle_update = false);
-
-  HitTestResult HitTestResultAtRect(
-      const LayoutRect&,
+  HitTestResult HitTestResultAtLocation(
+      const HitTestLocation&,
       HitTestRequest::HitTestRequestType hit_type = HitTestRequest::kReadOnly |
                                                     HitTestRequest::kActive,
       const LayoutObject* stop_node = nullptr,
@@ -196,10 +189,12 @@
   WebInputEventResult HandleGestureScrollEnd(const WebGestureEvent&);
   bool IsScrollbarHandlingGestures() const;
 
-  bool BestClickableNodeForHitTestResult(const HitTestResult&,
+  bool BestClickableNodeForHitTestResult(const HitTestLocation& location,
+                                         const HitTestResult&,
                                          IntPoint& target_point,
                                          Node*& target_node);
-  bool BestContextMenuNodeForHitTestResult(const HitTestResult&,
+  bool BestContextMenuNodeForHitTestResult(const HitTestLocation& location,
+                                           const HitTestResult&,
                                            IntPoint& target_point,
                                            Node*& target_node);
   void CacheTouchAdjustmentResult(uint32_t, FloatPoint);
@@ -277,8 +272,6 @@
     return *event_handler_registry_;
   }
 
-  void AnimateSnapFling(base::TimeTicks monotonic_time);
-
  private:
   enum NoCursorChangeType { kNoCursorChange };
 
@@ -303,10 +296,12 @@
       const WebMouseEvent&,
       const Vector<WebMouseEvent>&,
       HitTestResult* hovered_node = nullptr,
+      HitTestLocation* hit_test_location = nullptr,
       bool only_update_scrollbars = false,
       bool force_leave = false);
 
-  void ApplyTouchAdjustment(WebGestureEvent*, HitTestResult*);
+  // Updates the event, location and result to the adjusted target.
+  void ApplyTouchAdjustment(WebGestureEvent*, HitTestLocation&, HitTestResult*);
   WebInputEventResult HandleGestureTapDown(
       const GestureEventWithHitTestResults&);
   WebInputEventResult HandleGestureTap(const GestureEventWithHitTestResults&);
@@ -315,7 +310,9 @@
   WebInputEventResult HandleGestureLongTap(
       const GestureEventWithHitTestResults&);
 
-  void PerformHitTest(HitTestResult&, bool no_lifecycle_update) const;
+  void PerformHitTest(const HitTestLocation& location,
+                      HitTestResult&,
+                      bool no_lifecycle_update) const;
 
   void UpdateGestureTargetNodeForMouseEvent(
       const GestureEventWithHitTestResults&);
@@ -324,8 +321,9 @@
   bool GestureCorrespondsToAdjustedTouch(const WebGestureEvent&);
   bool IsSelectingLink(const HitTestResult&);
   bool ShouldShowIBeamForNode(const Node*, const HitTestResult&);
-  bool ShouldShowResizeForNode(const Node*, const HitTestResult&);
-  OptionalCursor SelectCursor(const HitTestResult&);
+  bool ShouldShowResizeForNode(const Node*, const HitTestLocation&);
+  OptionalCursor SelectCursor(const HitTestLocation& location,
+                              const HitTestResult&);
   OptionalCursor SelectAutoCursor(const HitTestResult&,
                                   Node*,
                                   const Cursor& i_beam);
@@ -356,7 +354,8 @@
       MouseEventWithHitTestResults&,
       const Vector<WebMouseEvent>&,
       LocalFrame* subframe,
-      HitTestResult* hovered_node = nullptr);
+      HitTestResult* hovered_node = nullptr,
+      HitTestLocation* hit_test_location = nullptr);
   WebInputEventResult PassMouseReleaseEventToSubframe(
       MouseEventWithHitTestResults&,
       LocalFrame* subframe);
diff --git a/third_party/blink/renderer/core/input/event_handler_test.cc b/third_party/blink/renderer/core/input/event_handler_test.cc
index 8c19446..399af96 100644
--- a/third_party/blink/renderer/core/input/event_handler_test.cc
+++ b/third_party/blink/renderer/core/input/event_handler_test.cc
@@ -348,9 +348,10 @@
 
 TEST_F(EventHandlerTest, HitOnNothingDoesNotShowIBeam) {
   SetHtmlInnerHTML("");
+  HitTestLocation location((LayoutPoint(10, 10)));
   HitTestResult hit =
-      GetDocument().GetFrame()->GetEventHandler().HitTestResultAtPoint(
-          LayoutPoint(10, 10));
+      GetDocument().GetFrame()->GetEventHandler().HitTestResultAtLocation(
+          location);
   EXPECT_FALSE(
       GetDocument().GetFrame()->GetEventHandler().ShouldShowIBeamForNode(
           GetDocument().body(), hit));
@@ -359,10 +360,10 @@
 TEST_F(EventHandlerTest, HitOnTextShowsIBeam) {
   SetHtmlInnerHTML("blabla");
   Node* const text = GetDocument().body()->firstChild();
-  LayoutPoint location =
-      text->GetLayoutObject()->FirstFragment().VisualRect().Center();
+  HitTestLocation location(
+      text->GetLayoutObject()->FirstFragment().VisualRect().Center());
   HitTestResult hit =
-      GetDocument().GetFrame()->GetEventHandler().HitTestResultAtPoint(
+      GetDocument().GetFrame()->GetEventHandler().HitTestResultAtLocation(
           location);
   EXPECT_TRUE(text->CanStartSelection());
   EXPECT_TRUE(
@@ -373,10 +374,10 @@
 TEST_F(EventHandlerTest, HitOnUserSelectNoneDoesNotShowIBeam) {
   SetHtmlInnerHTML("<span style='user-select: none'>blabla</span>");
   Node* const text = GetDocument().body()->firstChild()->firstChild();
-  LayoutPoint location =
-      text->GetLayoutObject()->FirstFragment().VisualRect().Center();
+  HitTestLocation location(
+      text->GetLayoutObject()->FirstFragment().VisualRect().Center());
   HitTestResult hit =
-      GetDocument().GetFrame()->GetEventHandler().HitTestResultAtPoint(
+      GetDocument().GetFrame()->GetEventHandler().HitTestResultAtLocation(
           location);
   EXPECT_FALSE(text->CanStartSelection());
   EXPECT_FALSE(
@@ -390,10 +391,10 @@
       "<span style='user-select: text' id='bla'>blabla</span>", "host");
 
   Node* const text = shadow_root->getElementById("bla")->firstChild();
-  LayoutPoint location =
-      text->GetLayoutObject()->FirstFragment().VisualRect().Center();
+  HitTestLocation location(
+      text->GetLayoutObject()->FirstFragment().VisualRect().Center());
   HitTestResult hit =
-      GetDocument().GetFrame()->GetEventHandler().HitTestResultAtPoint(
+      GetDocument().GetFrame()->GetEventHandler().HitTestResultAtLocation(
           location);
   EXPECT_TRUE(text->CanStartSelection());
   EXPECT_TRUE(
@@ -408,10 +409,10 @@
       "</div>");
   Node* const text =
       GetDocument().body()->firstChild()->firstChild()->firstChild();
-  LayoutPoint location =
-      text->GetLayoutObject()->FirstFragment().VisualRect().Center();
+  HitTestLocation location(
+      text->GetLayoutObject()->FirstFragment().VisualRect().Center());
   HitTestResult hit =
-      GetDocument().GetFrame()->GetEventHandler().HitTestResultAtPoint(
+      GetDocument().GetFrame()->GetEventHandler().HitTestResultAtLocation(
           location);
   EXPECT_TRUE(text->CanStartSelection());
   EXPECT_TRUE(
@@ -426,10 +427,10 @@
       "</div>");
   Node* const text =
       GetDocument().body()->firstChild()->firstChild()->firstChild();
-  LayoutPoint location =
-      text->GetLayoutObject()->FirstFragment().VisualRect().Center();
+  HitTestLocation location(
+      text->GetLayoutObject()->FirstFragment().VisualRect().Center());
   HitTestResult hit =
-      GetDocument().GetFrame()->GetEventHandler().HitTestResultAtPoint(
+      GetDocument().GetFrame()->GetEventHandler().HitTestResultAtLocation(
           location);
   EXPECT_FALSE(text->CanStartSelection());
   EXPECT_FALSE(
@@ -444,10 +445,10 @@
       "</div>");
   Node* const text =
       GetDocument().body()->firstChild()->firstChild()->firstChild();
-  LayoutPoint location =
-      text->GetLayoutObject()->FirstFragment().VisualRect().Center();
+  HitTestLocation location(
+      text->GetLayoutObject()->FirstFragment().VisualRect().Center());
   HitTestResult hit =
-      GetDocument().GetFrame()->GetEventHandler().HitTestResultAtPoint(
+      GetDocument().GetFrame()->GetEventHandler().HitTestResultAtLocation(
           location);
   EXPECT_TRUE(text->CanStartSelection());
   EXPECT_TRUE(
@@ -461,10 +462,10 @@
       "<span style='user-select: none'>blabla</span>"
       "</div>");
   Node* const text = GetDocument().body()->firstChild()->firstChild()->firstChild();
-  LayoutPoint location =
-      text->GetLayoutObject()->FirstFragment().VisualRect().Center();
+  HitTestLocation location(
+      text->GetLayoutObject()->FirstFragment().VisualRect().Center());
   HitTestResult hit =
-      GetDocument().GetFrame()->GetEventHandler().HitTestResultAtPoint(
+      GetDocument().GetFrame()->GetEventHandler().HitTestResultAtLocation(
           location);
   EXPECT_FALSE(text->CanStartSelection());
   EXPECT_FALSE(
@@ -478,10 +479,10 @@
       "<span style='user-select: none' id='bla'>blabla</span>", "host");
 
   Node* const text = shadow_root->getElementById("bla")->firstChild();
-  LayoutPoint location =
-      text->GetLayoutObject()->FirstFragment().VisualRect().Center();
+  HitTestLocation location(
+      text->GetLayoutObject()->FirstFragment().VisualRect().Center());
   HitTestResult hit =
-      GetDocument().GetFrame()->GetEventHandler().HitTestResultAtPoint(
+      GetDocument().GetFrame()->GetEventHandler().HitTestResultAtLocation(
           location);
   EXPECT_FALSE(text->CanStartSelection());
   EXPECT_FALSE(
@@ -493,10 +494,10 @@
   SetHtmlInnerHTML("<input value='blabla'>");
   auto* const field = ToHTMLInputElement(GetDocument().body()->firstChild());
   Element* const text = field->InnerEditorElement();
-  LayoutPoint location =
-      text->GetLayoutObject()->FirstFragment().VisualRect().Center();
+  HitTestLocation location(
+      text->GetLayoutObject()->FirstFragment().VisualRect().Center());
   HitTestResult hit =
-      GetDocument().GetFrame()->GetEventHandler().HitTestResultAtPoint(
+      GetDocument().GetFrame()->GetEventHandler().HitTestResultAtLocation(
           location);
   EXPECT_TRUE(text->CanStartSelection());
   EXPECT_TRUE(
@@ -513,10 +514,10 @@
       ToHTMLInputElement(GetDocument().getElementById("sample"));
   Node* const text = input->InnerEditorElement()->firstChild();
 
-  LayoutPoint location =
-      text->GetLayoutObject()->FirstFragment().VisualRect().Center();
+  HitTestLocation location(
+      text->GetLayoutObject()->FirstFragment().VisualRect().Center());
   HitTestResult hit =
-      GetDocument().GetFrame()->GetEventHandler().HitTestResultAtPoint(
+      GetDocument().GetFrame()->GetEventHandler().HitTestResultAtLocation(
           location);
   EXPECT_TRUE(text->CanStartSelection());
   EXPECT_TRUE(
@@ -527,10 +528,10 @@
 TEST_F(EventHandlerTest, ImagesCannotStartSelection) {
   SetHtmlInnerHTML("<img>");
   Element* const img = ToElement(GetDocument().body()->firstChild());
-  LayoutPoint location =
-      img->GetLayoutObject()->FirstFragment().VisualRect().Center();
+  HitTestLocation location(
+      img->GetLayoutObject()->FirstFragment().VisualRect().Center());
   HitTestResult hit =
-      GetDocument().GetFrame()->GetEventHandler().HitTestResultAtPoint(
+      GetDocument().GetFrame()->GetEventHandler().HitTestResultAtLocation(
           location);
   EXPECT_FALSE(img->CanStartSelection());
   EXPECT_FALSE(
@@ -541,24 +542,24 @@
 TEST_F(EventHandlerTest, AnchorTextCannotStartSelection) {
   SetHtmlInnerHTML("<a href='bala'>link text</a>");
   Node* const link = GetDocument().body()->firstChild();
-  LayoutPoint location =
-      link->GetLayoutObject()->FirstFragment().VisualRect().Center();
-  HitTestResult hit =
-      GetDocument().GetFrame()->GetEventHandler().HitTestResultAtPoint(
+  HitTestLocation location(
+      link->GetLayoutObject()->FirstFragment().VisualRect().Center());
+  HitTestResult result =
+      GetDocument().GetFrame()->GetEventHandler().HitTestResultAtLocation(
           location);
   Node* const text = link->firstChild();
   EXPECT_FALSE(text->CanStartSelection());
-  EXPECT_TRUE(hit.IsOverLink());
+  EXPECT_TRUE(result.IsOverLink());
   // ShouldShowIBeamForNode() returns |cursor: auto|'s value.
   // In https://github.com/w3c/csswg-drafts/issues/1598 it was decided that:
   // a { cursor: auto } /* gives I-beam over links */
   EXPECT_TRUE(
-      GetDocument().GetFrame()->GetEventHandler().ShouldShowIBeamForNode(text,
-                                                                         hit));
+      GetDocument().GetFrame()->GetEventHandler().ShouldShowIBeamForNode(
+          text, result));
   EXPECT_EQ(GetDocument()
                 .GetFrame()
                 ->GetEventHandler()
-                .SelectCursor(hit)
+                .SelectCursor(location, result)
                 .GetCursor()
                 .GetType(),
             Cursor::Type::kHand);  // A hand signals ability to navigate.
@@ -567,21 +568,21 @@
 TEST_F(EventHandlerTest, EditableAnchorTextCanStartSelection) {
   SetHtmlInnerHTML("<a contenteditable='true' href='bala'>editable link</a>");
   Node* const link = GetDocument().body()->firstChild();
-  LayoutPoint location =
-      link->GetLayoutObject()->FirstFragment().VisualRect().Center();
-  HitTestResult hit =
-      GetDocument().GetFrame()->GetEventHandler().HitTestResultAtPoint(
+  HitTestLocation location(
+      link->GetLayoutObject()->FirstFragment().VisualRect().Center());
+  HitTestResult result =
+      GetDocument().GetFrame()->GetEventHandler().HitTestResultAtLocation(
           location);
   Node* const text = link->firstChild();
   EXPECT_TRUE(text->CanStartSelection());
-  EXPECT_TRUE(hit.IsOverLink());
+  EXPECT_TRUE(result.IsOverLink());
   EXPECT_TRUE(
-      GetDocument().GetFrame()->GetEventHandler().ShouldShowIBeamForNode(text,
-                                                                         hit));
+      GetDocument().GetFrame()->GetEventHandler().ShouldShowIBeamForNode(
+          text, result));
   EXPECT_EQ(GetDocument()
                 .GetFrame()
                 ->GetEventHandler()
-                .SelectCursor(hit)
+                .SelectCursor(location, result)
                 .GetCursor()
                 .GetType(),
             Cursor::Type::kIBeam);  // An I-beam signals editability.
diff --git a/third_party/blink/renderer/core/input/event_handling_util.cc b/third_party/blink/renderer/core/input/event_handling_util.cc
index b89b56c..1f9ea6f 100644
--- a/third_party/blink/renderer/core/input/event_handling_util.cc
+++ b/third_party/blink/renderer/core/input/event_handling_util.cc
@@ -20,7 +20,8 @@
     LocalFrame* frame,
     const LayoutPoint& point,
     HitTestRequest::HitTestRequestType hit_type) {
-  HitTestResult result(HitTestRequest(hit_type), point);
+  HitTestLocation location(point);
+  HitTestResult result(HitTestRequest(hit_type), location);
 
   if (!frame || !frame->ContentLayoutObject())
     return result;
@@ -29,7 +30,7 @@
     if (!rect.Contains(RoundedIntPoint(point)))
       return result;
   }
-  frame->ContentLayoutObject()->HitTest(result);
+  frame->ContentLayoutObject()->HitTest(location, result);
   return result;
 }
 
diff --git a/third_party/blink/renderer/core/input/gesture_manager.cc b/third_party/blink/renderer/core/input/gesture_manager.cc
index fa870fdb..b43ff23 100644
--- a/third_party/blink/renderer/core/input/gesture_manager.cc
+++ b/third_party/blink/renderer/core/input/gesture_manager.cc
@@ -155,6 +155,8 @@
   uint64_t pre_dispatch_style_version = frame_->GetDocument()->StyleVersion();
 
   HitTestResult current_hit_test = targeted_event.GetHitTestResult();
+  const HitTestLocation& current_hit_test_location =
+      targeted_event.GetHitTestLocation();
 
   // We use the adjusted position so the application isn't surprised to see a
   // event with co-ordinates outside the target's bounds.
@@ -237,7 +239,8 @@
     }
     if (mouse_down_event_result == WebInputEventResult::kNotHandled) {
       mouse_down_event_result = mouse_event_manager_->HandleMousePressEvent(
-          MouseEventWithHitTestResults(fake_mouse_down, current_hit_test));
+          MouseEventWithHitTestResults(
+              fake_mouse_down, current_hit_test_location, current_hit_test));
     }
   }
 
@@ -292,9 +295,11 @@
     mouse_event_manager_->SetClickElement(nullptr);
   }
 
-  if (mouse_up_event_result == WebInputEventResult::kNotHandled)
+  if (mouse_up_event_result == WebInputEventResult::kNotHandled) {
     mouse_up_event_result = mouse_event_manager_->HandleMouseReleaseEvent(
-        MouseEventWithHitTestResults(fake_mouse_up, current_hit_test));
+        MouseEventWithHitTestResults(fake_mouse_up, current_hit_test_location,
+                                     current_hit_test));
+  }
   mouse_event_manager_->ClearDragHeuristicState();
 
   WebInputEventResult event_result = EventHandlingUtil::MergeEventResult(
@@ -326,10 +331,10 @@
   // overhaul of the touch drag-and-drop code and LongPress is such a special
   // scenario that it's unlikely to matter much in practice.
 
-  IntPoint hit_test_point = frame_->View()->ConvertFromRootFrame(
-      FlooredIntPoint(gesture_event.PositionInRootFrame()));
+  HitTestLocation location(frame_->View()->ConvertFromRootFrame(
+      FlooredIntPoint(gesture_event.PositionInRootFrame())));
   HitTestResult hit_test_result =
-      frame_->GetEventHandler().HitTestResultAtPoint(hit_test_point);
+      frame_->GetEventHandler().HitTestResultAtLocation(location);
 
   long_tap_should_invoke_context_menu_ = false;
   bool hit_test_contains_links = hit_test_result.URLElement() ||
diff --git a/third_party/blink/renderer/core/input/mouse_event_manager.cc b/third_party/blink/renderer/core/input/mouse_event_manager.cc
index 5b90829c..648ce2e 100644
--- a/third_party/blink/renderer/core/input/mouse_event_manager.cc
+++ b/third_party/blink/renderer/core/input/mouse_event_manager.cc
@@ -868,8 +868,9 @@
 
   if (mouse_down_may_start_drag_) {
     HitTestRequest request(HitTestRequest::kReadOnly);
-    HitTestResult result(request, mouse_down_pos_);
-    frame_->ContentLayoutObject()->HitTest(result);
+    HitTestLocation location(mouse_down_pos_);
+    HitTestResult result(request, location);
+    frame_->ContentLayoutObject()->HitTest(location, result);
     Node* node = result.InnerNode();
     if (node) {
       DragController::SelectionDragPolicy selection_drag_policy =
diff --git a/third_party/blink/renderer/core/input/mouse_wheel_event_manager.cc b/third_party/blink/renderer/core/input/mouse_wheel_event_manager.cc
index 415f42bcf..ac3feb5 100644
--- a/third_party/blink/renderer/core/input/mouse_wheel_event_manager.cc
+++ b/third_party/blink/renderer/core/input/mouse_wheel_event_manager.cc
@@ -148,8 +148,9 @@
       view->ConvertFromRootFrame(FlooredIntPoint(event.PositionInRootFrame()));
 
   HitTestRequest request(HitTestRequest::kReadOnly);
-  HitTestResult result(request, v_point);
-  doc->GetLayoutView()->HitTest(result);
+  HitTestLocation location(v_point);
+  HitTestResult result(request, location);
+  doc->GetLayoutView()->HitTest(location, result);
 
   Node* node = result.InnerNode();
   // Wheel events should not dispatch to text nodes.
diff --git a/third_party/blink/renderer/core/input/pointer_event_manager.cc b/third_party/blink/renderer/core/input/pointer_event_manager.cc
index e48a89c..1a98185 100644
--- a/third_party/blink/renderer/core/input/pointer_event_manager.cc
+++ b/third_party/blink/renderer/core/input/pointer_event_manager.cc
@@ -341,13 +341,13 @@
   // TODO(szager): Shouldn't this be PositionInScreen() ?
   LayoutPoint hit_test_point((FloatPoint)pointer_event.PositionInWidget());
   hit_test_point.Move(-hit_rect_size * 0.5f);
+  HitTestLocation location(LayoutRect(hit_test_point, hit_rect_size));
   HitTestResult hit_test_result =
-      root_frame.GetEventHandler().HitTestResultAtRect(
-          LayoutRect(hit_test_point, hit_rect_size), hit_type);
+      root_frame.GetEventHandler().HitTestResultAtLocation(location, hit_type);
   Node* adjusted_node = nullptr;
   IntPoint adjusted_point;
   bool adjusted = frame_->GetEventHandler().BestClickableNodeForHitTestResult(
-      hit_test_result, adjusted_point, adjusted_node);
+      location, hit_test_result, adjusted_point, adjusted_node);
 
   if (adjusted)
     pointer_event.SetPositionInWidget(adjusted_point.X(), adjusted_point.Y());
@@ -372,10 +372,10 @@
     HitTestRequest::HitTestRequestType hit_type = HitTestRequest::kTouchEvent |
                                                   HitTestRequest::kReadOnly |
                                                   HitTestRequest::kActive;
-    LayoutPoint page_point = frame_->View()->ConvertFromRootFrame(
-        LayoutPoint(web_pointer_event.PositionInWidget()));
+    HitTestLocation location(frame_->View()->ConvertFromRootFrame(
+        LayoutPoint(web_pointer_event.PositionInWidget())));
     HitTestResult hit_test_tesult =
-        frame_->GetEventHandler().HitTestResultAtPoint(page_point, hit_type);
+        frame_->GetEventHandler().HitTestResultAtLocation(location, hit_type);
     Node* node = hit_test_tesult.InnerNode();
     if (node) {
       pointer_event_target.target_frame = node->GetDocument().GetFrame();
diff --git a/third_party/blink/renderer/core/input/scroll_manager.cc b/third_party/blink/renderer/core/input/scroll_manager.cc
index 6c80d05e..896ab91 100644
--- a/third_party/blink/renderer/core/input/scroll_manager.cc
+++ b/third_party/blink/renderer/core/input/scroll_manager.cc
@@ -17,7 +17,6 @@
 #include "third_party/blink/renderer/core/layout/layout_embedded_content.h"
 #include "third_party/blink/renderer/core/layout/layout_view.h"
 #include "third_party/blink/renderer/core/page/autoscroll_controller.h"
-#include "third_party/blink/renderer/core/page/chrome_client.h"
 #include "third_party/blink/renderer/core/page/page.h"
 #include "third_party/blink/renderer/core/page/scrolling/overscroll_controller.h"
 #include "third_party/blink/renderer/core/page/scrolling/root_scroller_controller.h"
@@ -31,38 +30,8 @@
 #include "third_party/blink/renderer/platform/scroll/scroll_customization.h"
 
 namespace blink {
-namespace {
-
-SnapFlingController::GestureScrollType ToGestureScrollType(
-    WebInputEvent::Type web_event_type) {
-  switch (web_event_type) {
-    case WebInputEvent::kGestureScrollBegin:
-      return SnapFlingController::GestureScrollType::kBegin;
-    case WebInputEvent::kGestureScrollUpdate:
-      return SnapFlingController::GestureScrollType::kUpdate;
-    case WebInputEvent::kGestureScrollEnd:
-      return SnapFlingController::GestureScrollType::kEnd;
-    default:
-      NOTREACHED();
-      return SnapFlingController::GestureScrollType::kBegin;
-  }
-}
-
-SnapFlingController::GestureScrollUpdateInfo GetGestureScrollUpdateInfo(
-    const WebGestureEvent& event) {
-  return {.delta = gfx::Vector2dF(-event.data.scroll_update.delta_x,
-                                  -event.data.scroll_update.delta_y),
-          .is_in_inertial_phase = event.data.scroll_update.inertial_phase ==
-                                  WebGestureEvent::kMomentumPhase,
-          .event_time = event.TimeStamp()};
-}
-
-}  // namespace
 
 ScrollManager::ScrollManager(LocalFrame& frame) : frame_(frame) {
-  if (RuntimeEnabledFeatures::CSSScrollSnapPointsEnabled())
-    snap_fling_controller_ = std::make_unique<cc::SnapFlingController>(this);
-
   Clear();
 }
 
@@ -487,12 +456,6 @@
       return WebInputEventResult::kNotHandled;
     }
   }
-  if (snap_fling_controller_) {
-    if (snap_fling_controller_->HandleGestureScrollUpdate(
-            GetGestureScrollUpdateInfo(gesture_event))) {
-      return WebInputEventResult::kHandledSystem;
-    }
-  }
 
   // Negate the deltas since the gesture event stores finger movement and
   // scrolling occurs in the direction opposite the finger's movement
@@ -575,6 +538,25 @@
   return WebInputEventResult::kNotHandled;
 }
 
+void ScrollManager::SnapAtGestureScrollEnd() {
+  if (!previous_gesture_scrolled_element_)
+    return;
+  SnapCoordinator* snap_coordinator =
+      frame_->GetDocument()->GetSnapCoordinator();
+  Element* document_element = frame_->GetDocument()->documentElement();
+  LayoutBox* layout_box =
+      previous_gesture_scrolled_element_ == document_element
+          ? frame_->GetDocument()->GetLayoutView()
+          : previous_gesture_scrolled_element_->GetLayoutBox();
+  if (!snap_coordinator || !layout_box || !layout_box->GetScrollableArea() ||
+      (!did_scroll_x_for_scroll_gesture_ && !did_scroll_y_for_scroll_gesture_))
+    return;
+
+  snap_coordinator->PerformSnapping(*layout_box,
+                                    did_scroll_x_for_scroll_gesture_,
+                                    did_scroll_y_for_scroll_gesture_);
+}
+
 WebInputEventResult ScrollManager::HandleGestureScrollEnd(
     const WebGestureEvent& gesture_event) {
   TRACE_EVENT0("input", "ScrollManager::handleGestureScrollEnd");
@@ -607,101 +589,6 @@
   return WebInputEventResult::kNotHandled;
 }
 
-LayoutBox* ScrollManager::LayoutBoxForSnapping() const {
-  Element* document_element = frame_->GetDocument()->documentElement();
-  return previous_gesture_scrolled_element_ == document_element
-             ? frame_->GetDocument()->GetLayoutView()
-             : previous_gesture_scrolled_element_->GetLayoutBox();
-}
-
-ScrollableArea* ScrollManager::ScrollableAreaForSnapping() const {
-  Element* document_element = frame_->GetDocument()->documentElement();
-  return previous_gesture_scrolled_element_ == document_element
-             ? frame_->View()->GetScrollableArea()
-             : previous_gesture_scrolled_element_->GetLayoutBox()
-                   ->GetScrollableArea();
-}
-
-void ScrollManager::SnapAtGestureScrollEnd() {
-  if (!previous_gesture_scrolled_element_ ||
-      (!did_scroll_x_for_scroll_gesture_ && !did_scroll_y_for_scroll_gesture_))
-    return;
-  SnapCoordinator* snap_coordinator =
-      frame_->GetDocument()->GetSnapCoordinator();
-  if (!snap_coordinator)
-    return;
-
-  LayoutBox* layout_box = LayoutBoxForSnapping();
-  snap_coordinator->PerformSnapping(*layout_box,
-                                    did_scroll_x_for_scroll_gesture_,
-                                    did_scroll_y_for_scroll_gesture_);
-}
-
-bool ScrollManager::GetSnapFlingInfo(const gfx::Vector2dF& natural_displacement,
-                                     gfx::Vector2dF* out_initial_offset,
-                                     gfx::Vector2dF* out_target_offset) const {
-  if (!previous_gesture_scrolled_element_)
-    return false;
-
-  SnapCoordinator* snap_coordinator =
-      frame_->GetDocument()->GetSnapCoordinator();
-  LayoutBox* layout_box = LayoutBoxForSnapping();
-  if (!snap_coordinator || !layout_box || !layout_box->GetScrollableArea())
-    return false;
-  return snap_coordinator->GetSnapFlingInfo(
-      *layout_box, natural_displacement, out_initial_offset, out_target_offset);
-}
-
-gfx::Vector2dF ScrollManager::ScrollByForSnapFling(
-    const gfx::Vector2dF& delta) {
-  DCHECK(previous_gesture_scrolled_element_);
-  std::unique_ptr<ScrollStateData> scroll_state_data =
-      std::make_unique<ScrollStateData>();
-
-  // TODO(sunyunjia): Plumb the velocity of the snap curve as well.
-  scroll_state_data->delta_x = delta.x();
-  scroll_state_data->delta_y = delta.y();
-  scroll_state_data->is_in_inertial_phase = true;
-  scroll_state_data->from_user_input = true;
-  scroll_state_data->delta_granularity =
-      static_cast<double>(ScrollGranularity::kScrollByPrecisePixel);
-  scroll_state_data->delta_consumed_for_scroll_sequence =
-      delta_consumed_for_scroll_sequence_;
-  ScrollState* scroll_state = ScrollState::Create(std::move(scroll_state_data));
-  scroll_state->SetCurrentNativeScrollingElement(
-      previous_gesture_scrolled_element_);
-
-  CustomizedScroll(*scroll_state);
-
-  ScrollableArea* scrollable_area = ScrollableAreaForSnapping();
-  FloatPoint end_position = scrollable_area->ScrollPosition();
-  return gfx::Vector2dF(end_position.X(), end_position.Y());
-}
-
-void ScrollManager::ScrollEndForSnapFling() {
-  std::unique_ptr<ScrollStateData> scroll_state_data =
-      std::make_unique<ScrollStateData>();
-  scroll_state_data->is_ending = true;
-  scroll_state_data->is_in_inertial_phase = true;
-  scroll_state_data->from_user_input = true;
-  scroll_state_data->delta_consumed_for_scroll_sequence =
-      delta_consumed_for_scroll_sequence_;
-  ScrollState* scroll_state = ScrollState::Create(std::move(scroll_state_data));
-  CustomizedScroll(*scroll_state);
-  NotifyScrollPhaseEndForCustomizedScroll();
-  ClearGestureScrollState();
-}
-
-void ScrollManager::RequestAnimationForSnapFling() {
-  if (Page* page = frame_->GetPage())
-    page->GetChromeClient().ScheduleAnimation(frame_->View());
-}
-
-void ScrollManager::AnimateSnapFling(base::TimeTicks monotonic_time) {
-  DCHECK(snap_fling_controller_);
-  snap_fling_controller_->Animate(monotonic_time);
-}
-
 Page* ScrollManager::GetPage() const {
   return frame_->GetPage();
 }
@@ -763,8 +650,9 @@
     LayoutPoint view_point = view->ConvertFromRootFrame(
         FlooredIntPoint(gesture_event.PositionInRootFrame()));
     HitTestRequest request(HitTestRequest::kReadOnly);
-    HitTestResult result(request, view_point);
-    document->GetLayoutView()->HitTest(result);
+    HitTestLocation location(view_point);
+    HitTestResult result(request, location);
+    document->GetLayoutView()->HitTest(location, result);
 
     event_target = result.InnerNode();
 
@@ -809,13 +697,6 @@
     }
   }
 
-  if (snap_fling_controller_) {
-    if (gesture_event.IsGestureScroll() &&
-        (snap_fling_controller_->FilterEventForSnap(
-            ToGestureScrollType(gesture_event.GetType())))) {
-      return WebInputEventResult::kNotHandled;
-    }
-  }
   switch (gesture_event.GetType()) {
     case WebInputEvent::kGestureScrollBegin:
       return HandleGestureScrollBegin(gesture_event);
diff --git a/third_party/blink/renderer/core/input/scroll_manager.h b/third_party/blink/renderer/core/input/scroll_manager.h
index 72f0136..3849f498 100644
--- a/third_party/blink/renderer/core/input/scroll_manager.h
+++ b/third_party/blink/renderer/core/input/scroll_manager.h
@@ -14,7 +14,6 @@
 #include "third_party/blink/renderer/platform/geometry/layout_size.h"
 #include "third_party/blink/renderer/platform/heap/handle.h"
 #include "third_party/blink/renderer/platform/heap/visitor.h"
-#include "third_party/blink/renderer/platform/scroll/scroll_snap_data.h"
 #include "third_party/blink/renderer/platform/scroll/scroll_types.h"
 #include "third_party/blink/renderer/platform/wtf/allocator.h"
 
@@ -27,7 +26,6 @@
 class PaintLayer;
 class PaintLayerScrollableArea;
 class Page;
-class ScrollableArea;
 class Scrollbar;
 class ScrollState;
 class WebGestureEvent;
@@ -36,11 +34,9 @@
 // user action that causes scrolling or resizing is determined in other *Manager
 // classes and they call into this class for doing the work.
 class CORE_EXPORT ScrollManager
-    : public GarbageCollectedFinalized<ScrollManager>,
-      public SnapFlingClient {
+    : public GarbageCollectedFinalized<ScrollManager> {
  public:
   explicit ScrollManager(LocalFrame&);
-  virtual ~ScrollManager() = default;
   void Trace(blink::Visitor*);
 
   void Clear();
@@ -94,16 +90,6 @@
   void ClearResizeScrollableArea(bool should_not_be_null);
   void SetResizeScrollableArea(PaintLayer*, IntPoint);
 
-  // SnapFlingClient implementation.
-  bool GetSnapFlingInfo(const gfx::Vector2dF& natural_displacement,
-                        gfx::Vector2dF* out_initial_offset,
-                        gfx::Vector2dF* out_target_offset) const override;
-  gfx::Vector2dF ScrollByForSnapFling(const gfx::Vector2dF& delta) override;
-  void ScrollEndForSnapFling() override;
-  void RequestAnimationForSnapFling() override;
-
-  void AnimateSnapFling(base::TimeTicks monotonic_time);
-
  private:
   WebInputEventResult HandleGestureScrollUpdate(const WebGestureEvent&);
   WebInputEventResult HandleGestureScrollBegin(const WebGestureEvent&);
@@ -139,9 +125,6 @@
   void NotifyScrollPhaseBeginForCustomizedScroll(const ScrollState&);
   void NotifyScrollPhaseEndForCustomizedScroll();
 
-  LayoutBox* LayoutBoxForSnapping() const;
-  ScrollableArea* ScrollableAreaForSnapping() const;
-
   // NOTE: If adding a new field to this class please ensure that it is
   // cleared in |ScrollManager::clear()|.
 
@@ -176,8 +159,6 @@
 
   Member<PaintLayerScrollableArea> resize_scrollable_area_;
 
-  std::unique_ptr<SnapFlingController> snap_fling_controller_;
-
   LayoutSize
       offset_from_resize_corner_;  // In the coords of m_resizeScrollableArea.
 
diff --git a/third_party/blink/renderer/core/input/scroll_snap_test.cc b/third_party/blink/renderer/core/input/scroll_snap_test.cc
index e0b7f9c..2b7f3f2 100644
--- a/third_party/blink/renderer/core/input/scroll_snap_test.cc
+++ b/third_party/blink/renderer/core/input/scroll_snap_test.cc
@@ -21,12 +21,8 @@
   // the pointer/finger's location on touch screen.
   void GestureScroll(double x, double y, double delta_x, double delta_y);
   void ScrollBegin(double x, double y, double hint_x, double hint_y);
-  void ScrollUpdate(double x,
-                    double y,
-                    double delta_x,
-                    double delta_y,
-                    bool is_in_inertial_phase = false);
-  void ScrollEnd(double x, double y, bool is_in_inertial_phase = false);
+  void ScrollUpdate(double x, double y, double delta_x, double delta_y);
+  void ScrollEnd(double x, double y);
   void SetInitialScrollOffset(double x, double y);
 };
 
@@ -103,8 +99,7 @@
 void ScrollSnapTest::ScrollUpdate(double x,
                                   double y,
                                   double delta_x,
-                                  double delta_y,
-                                  bool is_in_inertial_phase) {
+                                  double delta_y) {
   WebGestureEvent event(WebInputEvent::kGestureScrollUpdate,
                         WebInputEvent::kNoModifiers, CurrentTimeTicks(),
                         WebGestureDevice::kWebGestureDeviceTouchscreen);
@@ -112,23 +107,16 @@
   event.SetPositionInScreen(WebFloatPoint(x, y));
   event.data.scroll_update.delta_x = delta_x;
   event.data.scroll_update.delta_y = delta_y;
-  if (is_in_inertial_phase) {
-    event.data.scroll_update.inertial_phase = WebGestureEvent::kMomentumPhase;
-    event.SetTimeStamp(base::TimeTicks());
-  }
   event.SetFrameScale(1);
   GetDocument().GetFrame()->GetEventHandler().HandleGestureScrollEvent(event);
 }
 
-void ScrollSnapTest::ScrollEnd(double x, double y, bool is_in_inertial_phase) {
+void ScrollSnapTest::ScrollEnd(double x, double y) {
   WebGestureEvent event(WebInputEvent::kGestureScrollEnd,
                         WebInputEvent::kNoModifiers, CurrentTimeTicks(),
                         WebGestureDevice::kWebGestureDeviceTouchscreen);
   event.SetPositionInWidget(WebFloatPoint(x, y));
   event.SetPositionInScreen(WebFloatPoint(x, y));
-  event.data.scroll_end.inertial_phase =
-      is_in_inertial_phase ? WebGestureEvent::kMomentumPhase
-                           : WebGestureEvent::kNonMomentumPhase;
   GetDocument().GetFrame()->GetEventHandler().HandleGestureScrollEvent(event);
 }
 
@@ -175,33 +163,6 @@
   ASSERT_EQ(scroller->scrollTop(), 200);
 }
 
-TEST_F(ScrollSnapTest, AnimateFlingToArriveAtSnapPoint) {
-  SetUpForDiv();
-  // Vertically align with the area.
-  SetInitialScrollOffset(0, 200);
-  Element* scroller = GetDocument().getElementById("scroller");
-  ASSERT_EQ(scroller->scrollLeft(), 0);
-  ASSERT_EQ(scroller->scrollTop(), 200);
-
-  ScrollBegin(100, 100, -5, 0);
-  // Starts with a non-inertial GSU.
-  ScrollUpdate(100, 100, -5, 0);
-  // Fling with an inertial GSU.
-  ScrollUpdate(95, 100, -5, 0, true);
-  ScrollEnd(90, 100);
-  Compositor().BeginFrame();
-  // Animate halfway through the fling.
-  Compositor().BeginFrame(0.2);
-  ASSERT_GT(scroller->scrollLeft(), 150);
-  ASSERT_LT(scroller->scrollLeft(), 180);
-  ASSERT_EQ(scroller->scrollTop(), 200);
-  // Finish the animation.
-  Compositor().BeginFrame(0.6);
-
-  ASSERT_EQ(scroller->scrollLeft(), 200);
-  ASSERT_EQ(scroller->scrollTop(), 200);
-}
-
 TEST_F(ScrollSnapTest, SnapWhenBodyViewportDefining) {
   v8::HandleScope HandleScope(v8::Isolate::GetCurrent());
   WebView().Resize(WebSize(300, 300));
diff --git a/third_party/blink/renderer/core/input/touch_action_test.cc b/third_party/blink/renderer/core/input/touch_action_test.cc
index 7234e44..632763a1 100644
--- a/third_party/blink/renderer/core/input/touch_action_test.cc
+++ b/third_party/blink/renderer/core/input/touch_action_test.cc
@@ -312,8 +312,10 @@
       // element we intended. This is the easiest way for a test to be broken,
       // but has nothing really to do with touch action.  Note that we can't use
       // WebView's hit test API because it doesn't look into shadow DOM.
-      HitTestResult result = main_frame->GetEventHandler().HitTestResultAtPoint(
-          window_point, HitTestRequest::kReadOnly | HitTestRequest::kActive);
+      HitTestLocation location(window_point);
+      HitTestResult result =
+          main_frame->GetEventHandler().HitTestResultAtLocation(
+              location, HitTestRequest::kReadOnly | HitTestRequest::kActive);
       ASSERT_EQ(element, result.InnerElement())
           << "Unexpected hit test result " << failure_context_pos
           << "  Got element: \""
diff --git a/third_party/blink/renderer/core/inspector/browser_protocol.pdl b/third_party/blink/renderer/core/inspector/browser_protocol.pdl
index c64dfd0..5743f1e 100644
--- a/third_party/blink/renderer/core/inspector/browser_protocol.pdl
+++ b/third_party/blink/renderer/core/inspector/browser_protocol.pdl
@@ -533,6 +533,9 @@
       # substring in their name are extracted. An empty or absent query returns
       # all histograms.
       optional string query
+      # If true, retrieve delta since last call.
+      optional boolean delta
+
     returns
       # Histograms.
       array of Histogram histograms
@@ -542,6 +545,8 @@
     parameters
       # Requested histogram name.
       string name
+      # If true, retrieve delta since last call.
+      optional boolean delta
     returns
       # Histogram.
       Histogram histogram
@@ -2092,6 +2097,124 @@
       # Attribute/property value.
       string value
 
+  # Index of the string in the strings table.
+  type StringIndex extends integer
+
+  # Index of the string in the strings table.
+  type ArrayOfStrings extends array of StringIndex
+
+  # Data that is only present on rare nodes.
+  type RareStringData extends object
+    properties
+      array of integer index
+      array of StringIndex value
+
+  type RareBooleanData extends object
+    properties
+      array of integer index
+
+  type RareIntegerData extends object
+    properties
+      array of integer index
+      array of integer value
+
+  type Rectangle extends array of number
+
+  # DOM tree snapshot.
+  type DOMTreeSnapshot extends object
+    properties
+      # Parent node index.
+      optional array of integer parentIndex
+      # `Node`'s nodeType.
+      optional array of integer nodeType
+      # `Node`'s nodeName.
+      optional array of StringIndex nodeName
+      # `Node`'s nodeValue.
+      optional array of StringIndex nodeValue
+      # `Node`'s id, corresponds to DOM.Node.backendNodeId.
+      optional array of DOM.BackendNodeId backendNodeId
+      # Attributes of an `Element` node. Flatten name, value pairs.
+      optional array of ArrayOfStrings attributes
+      # The index of the node's related layout tree node in the `layoutTreeNodes` array returned by
+      # `captureSnapshot`, if any.
+      optional array of integer layoutNodeIndex
+      # Only set for textarea elements, contains the text value.
+      optional RareStringData textValue
+      # Only set for input elements, contains the input's associated text value.
+      optional RareStringData inputValue
+      # Only set for radio and checkbox input elements, indicates if the element has been checked
+      optional RareBooleanData inputChecked
+      # Only set for option elements, indicates if the element has been selected
+      optional RareBooleanData optionSelected
+      # Document URL that `Document` or `FrameOwner` node points to.
+      optional RareStringData documentURL
+      # Base URL that `Document` or `FrameOwner` node uses for URL completion.
+      optional RareStringData baseURL
+      # Only set for documents, contains the document's content language.
+      optional RareStringData contentLanguage
+      # Only set for documents, contains the document's character set encoding.
+      optional RareStringData documentEncoding
+      # `DocumentType` node's publicId.
+      optional RareStringData publicId
+      # `DocumentType` node's systemId.
+      optional RareStringData systemId
+      # Frame ID for frame owner elements and also for the document node.
+      optional RareStringData frameId
+      # The index of a frame owner element's content document in the `domNodes` array returned by
+      # `captureSnapshot`, if any.
+      optional RareIntegerData contentDocumentIndex
+      # Index of the imported document's node of a link element in the `domNodes` array returned by
+      # `captureSnapshot`, if any.
+      optional RareIntegerData importedDocumentIndex
+      # Index of the content node of a template element in the `domNodes` array returned by
+      # `captureSnapshot`.
+      optional RareIntegerData templateContentIndex
+      # Type of a pseudo element node.
+      optional RareStringData pseudoType
+      # Whether this DOM node responds to mouse clicks. This includes nodes that have had click
+      # event listeners attached via JavaScript as well as anchor tags that naturally navigate when
+      # clicked.
+      optional RareBooleanData isClickable
+      # The selected url for nodes with a srcset attribute.
+      optional RareStringData currentSourceURL
+      # The url of the script (if any) that generates this node.
+      optional RareStringData originURL
+
+  # Details of post layout rendered text positions. The exact layout should not be regarded as
+  # stable and may change between versions.
+  type TextBoxSnapshot extends object
+    properties
+      # Intex of th elayout tree node that owns this box collection.
+      array of integer layoutIndex
+      # The absolute position bounding box.
+      array of Rectangle bounds
+      # The starting index in characters, for this post layout textbox substring. Characters that
+      # would be represented as a surrogate pair in UTF-16 have length 2.
+      array of integer start
+      # The number of characters in this post layout textbox substring. Characters that would be
+      # represented as a surrogate pair in UTF-16 have length 2.
+      array of integer length
+
+  # Details of an element in the DOM tree with a LayoutObject.
+  type LayoutTreeSnapshot extends object
+    properties
+      # The index of the related DOM node in the `domNodes` array returned by `getSnapshot`.
+      array of integer nodeIndex
+      # Index into the `computedStyles` array returned by `captureSnapshot`.
+      array of ArrayOfStrings styles
+      # The absolute position bounding box.
+      array of Rectangle bounds
+      # Contents of the LayoutText, if any.
+      array of StringIndex text
+      # The post-layout inline text nodes
+      TextBoxSnapshot textBoxes
+
+  # Computed style snapshot.
+  type StylesSnapshot extends object
+    properties
+      # Whitelisted ComputedStyle property values referenced by styleIndex.
+      array of ArrayOfStrings values
+
   # Disables DOM snapshot agent for the given page.
   command disable
 
@@ -2102,7 +2225,7 @@
   # template contents, and imported documents) in a flattened array, as well as layout and
   # white-listed computed style information for the nodes. Shadow DOM in the returned DOM tree is
   # flattened.
-  command getSnapshot
+  deprecated command getSnapshot
     parameters
       # Whitelist of computed styles to return.
       array of string computedStyleWhitelist
@@ -2120,6 +2243,22 @@
       # Whitelisted ComputedStyle properties for each node in the layout tree.
       array of ComputedStyle computedStyles
 
+  # Returns a document snapshot, including the full DOM tree of the root node (including iframes,
+  # template contents, and imported documents) in a flattened array, as well as layout and
+  # white-listed computed style information for the nodes. Shadow DOM in the returned DOM tree is
+  # flattened.
+  command captureSnapshot
+    parameters
+      # Whitelist of computed styles to return.
+      array of string computedStyles
+    returns
+      # The nodes in the DOM tree. The DOMNode at index 0 corresponds to the root document.
+      DOMTreeSnapshot nodes
+      # The nodes in the layout tree.
+      LayoutTreeSnapshot layout
+      # Shared string table that all string properties refer to with indexes.
+      array of string strings
+
 # Query and modify DOM storage.
 experimental domain DOMStorage
 
diff --git a/third_party/blink/renderer/core/inspector/inspector_css_agent.cc b/third_party/blink/renderer/core/inspector/inspector_css_agent.cc
index b83b6ad9..b0610223 100644
--- a/third_party/blink/renderer/core/inspector/inspector_css_agent.cc
+++ b/third_party/blink/renderer/core/inspector/inspector_css_agent.cc
@@ -152,8 +152,9 @@
                          HitTestRequest::kPenetratingList |
                          HitTestRequest::kIgnoreClipping);
 
-  HitTestResult result(request, rect);
-  document.GetFrame()->ContentLayoutObject()->HitTest(result);
+  HitTestLocation location(rect);
+  HitTestResult result(request, location);
+  document.GetFrame()->ContentLayoutObject()->HitTest(location, result);
   HeapVector<Member<Element>> elements;
   Node* previous_node = nullptr;
   for (const auto hit_test_result_node : result.ListBasedTestResult()) {
diff --git a/third_party/blink/renderer/core/inspector/inspector_dom_agent.cc b/third_party/blink/renderer/core/inspector/inspector_dom_agent.cc
index da63747..6e6dc1c 100644
--- a/third_party/blink/renderer/core/inspector/inspector_dom_agent.cc
+++ b/third_party/blink/renderer/core/inspector/inspector_dom_agent.cc
@@ -1327,9 +1327,9 @@
   LayoutPoint document_point(x, y);
   HitTestRequest request(HitTestRequest::kMove | HitTestRequest::kReadOnly |
                          HitTestRequest::kAllowChildFrameContent);
-  HitTestResult result(request,
-                       document_->View()->DocumentToFrame(document_point));
-  document_->GetFrame()->ContentLayoutObject()->HitTest(result);
+  HitTestLocation location(document_->View()->DocumentToFrame(document_point));
+  HitTestResult result(request, location);
+  document_->GetFrame()->ContentLayoutObject()->HitTest(location, result);
   if (!include_user_agent_shadow_dom)
     result.SetToShadowHostIfInRestrictedShadowRoot();
   Node* node = result.InnerPossiblyPseudoNode();
diff --git a/third_party/blink/renderer/core/inspector/inspector_dom_snapshot_agent.cc b/third_party/blink/renderer/core/inspector/inspector_dom_snapshot_agent.cc
index 2a1060f..e50eee5 100644
--- a/third_party/blink/renderer/core/inspector/inspector_dom_snapshot_agent.cc
+++ b/third_party/blink/renderer/core/inspector/inspector_dom_snapshot_agent.cc
@@ -63,6 +63,17 @@
       .build();
 }
 
+std::unique_ptr<protocol::Array<double>> BuildRectForFloatRect2(
+    const FloatRect& rect) {
+  std::unique_ptr<protocol::Array<double>> result =
+      protocol::Array<double>::create();
+  result->addItem(rect.X());
+  result->addItem(rect.Y());
+  result->addItem(rect.Width());
+  result->addItem(rect.Height());
+  return result;
+}
+
 Document* GetEmbeddedDocument(PaintLayer* layer) {
   // Documents are embedded on their own PaintLayer via a LayoutEmbeddedContent.
   if (layer->GetLayoutObject().IsLayoutEmbeddedContent()) {
@@ -76,6 +87,26 @@
   return nullptr;
 }
 
+std::unique_ptr<protocol::DOMSnapshot::RareStringData> StringData() {
+  return protocol::DOMSnapshot::RareStringData::create()
+      .setIndex(protocol::Array<int>::create())
+      .setValue(protocol::Array<int>::create())
+      .build();
+}
+
+std::unique_ptr<protocol::DOMSnapshot::RareIntegerData> IntegerData() {
+  return protocol::DOMSnapshot::RareIntegerData::create()
+      .setIndex(protocol::Array<int>::create())
+      .setValue(protocol::Array<int>::create())
+      .build();
+}
+
+std::unique_ptr<protocol::DOMSnapshot::RareBooleanData> BooleanData() {
+  return protocol::DOMSnapshot::RareBooleanData::create()
+      .setIndex(protocol::Array<int>::create())
+      .build();
+}
+
 }  // namespace
 
 struct InspectorDOMSnapshotAgent::VectorStringHashTraits
@@ -203,8 +234,6 @@
         layout_tree_nodes,
     std::unique_ptr<protocol::Array<protocol::DOMSnapshot::ComputedStyle>>*
         computed_styles) {
-  DCHECK(!dom_nodes_ && !layout_tree_nodes_ && !computed_styles_);
-
   Document* document = inspected_frames_->Root()->GetDocument();
   if (!document)
     return Response::Error("Document is not available");
@@ -247,6 +276,85 @@
   return Response::OK();
 }
 
+protocol::Response InspectorDOMSnapshotAgent::captureSnapshot(
+    std::unique_ptr<protocol::Array<String>> computed_styles,
+    std::unique_ptr<protocol::DOMSnapshot::DOMTreeSnapshot>* dom_tree_snapshot,
+    std::unique_ptr<protocol::DOMSnapshot::LayoutTreeSnapshot>*
+        layout_tree_snapshot,
+    std::unique_ptr<protocol::Array<String>>* strings) {
+  Document* document = inspected_frames_->Root()->GetDocument();
+  if (!document)
+    return Response::Error("Document is not available");
+
+  strings_ = protocol::Array<String>::create();
+
+  // Setup snapshot.
+  dom_tree_snapshot_ =
+      protocol::DOMSnapshot::DOMTreeSnapshot::create()
+          .setParentIndex(protocol::Array<int>::create())
+          .setNodeType(protocol::Array<int>::create())
+          .setNodeName(protocol::Array<int>::create())
+          .setNodeValue(protocol::Array<int>::create())
+          .setBackendNodeId(protocol::Array<int>::create())
+          .setAttributes(protocol::Array<protocol::Array<int>>::create())
+          .setLayoutNodeIndex(protocol::Array<int>::create())
+          .setTextValue(StringData())
+          .setInputValue(StringData())
+          .setInputChecked(BooleanData())
+          .setOptionSelected(BooleanData())
+          .setDocumentURL(StringData())
+          .setBaseURL(StringData())
+          .setContentLanguage(StringData())
+          .setDocumentEncoding(StringData())
+          .setPublicId(StringData())
+          .setSystemId(StringData())
+          .setFrameId(StringData())
+          .setContentDocumentIndex(IntegerData())
+          .setImportedDocumentIndex(IntegerData())
+          .setTemplateContentIndex(IntegerData())
+          .setPseudoType(StringData())
+          .setIsClickable(BooleanData())
+          .setCurrentSourceURL(StringData())
+          .setOriginURL(StringData())
+          .build();
+  layout_tree_snapshot_ =
+      protocol::DOMSnapshot::LayoutTreeSnapshot::create()
+          .setNodeIndex(protocol::Array<int>::create())
+          .setBounds(protocol::Array<protocol::Array<double>>::create())
+          .setText(protocol::Array<int>::create())
+          .setStyles(protocol::Array<protocol::Array<int>>::create())
+          .setTextBoxes(
+              protocol::DOMSnapshot::TextBoxSnapshot::create()
+                  .setLayoutIndex(protocol::Array<int>::create())
+                  .setBounds(protocol::Array<protocol::Array<double>>::create())
+                  .setStart(protocol::Array<int>::create())
+                  .setLength(protocol::Array<int>::create())
+                  .build())
+          .build();
+
+  css_property_whitelist_ = std::make_unique<CSSPropertyWhitelist>();
+
+  // Look up the CSSPropertyIDs for each entry in |computed_styles|.
+  for (size_t i = 0; i < computed_styles->length(); i++) {
+    CSSPropertyID property_id = cssPropertyID(computed_styles->get(i));
+    if (property_id == CSSPropertyInvalid)
+      continue;
+    css_property_whitelist_->push_back(
+        std::make_pair(computed_styles->get(i), property_id));
+  }
+
+  // Actual traversal.
+  VisitNode2(document, -1);
+
+  // Extract results from state and reset.
+  *dom_tree_snapshot = std::move(dom_tree_snapshot_);
+  *layout_tree_snapshot = std::move(layout_tree_snapshot_);
+  *strings = std::move(strings_);
+  css_property_whitelist_.reset();
+  string_table_.clear();
+  return Response::OK();
+}
+
 int InspectorDOMSnapshotAgent::VisitNode(Node* node,
                                          bool include_event_listeners,
                                          bool include_user_agent_shadow_tree) {
@@ -414,6 +522,200 @@
   return index;
 }
 
+int InspectorDOMSnapshotAgent::AddString(const String& string) {
+  if (string.IsEmpty())
+    return -1;
+  auto it = string_table_.find(string);
+  int index;
+  if (it == string_table_.end()) {
+    index = strings_->length();
+    strings_->addItem(string);
+    string_table_.Set(string, index);
+  } else {
+    index = it->value;
+  }
+  return index;
+}
+
+void InspectorDOMSnapshotAgent::SetRare(
+    protocol::DOMSnapshot::RareIntegerData* data,
+    int index,
+    int value) {
+  data->getIndex()->addItem(index);
+  data->getValue()->addItem(value);
+}
+
+void InspectorDOMSnapshotAgent::SetRare(
+    protocol::DOMSnapshot::RareStringData* data,
+    int index,
+    const String& value) {
+  data->getIndex()->addItem(index);
+  data->getValue()->addItem(AddString(value));
+}
+
+void InspectorDOMSnapshotAgent::SetRare(
+    protocol::DOMSnapshot::RareBooleanData* data,
+    int index) {
+  data->getIndex()->addItem(index);
+}
+
+int InspectorDOMSnapshotAgent::VisitNode2(Node* node, int parent_index) {
+  // Update layout tree before traversal of document so that we inspect a
+  // current and consistent state of all trees. No need to do this if paint
+  // order was calculated, since layout trees were already updated during
+  // TraversePaintLayerTree().
+  if (node->IsDocumentNode())
+    node->GetDocument().UpdateStyleAndLayoutTree();
+  String node_value;
+  switch (node->getNodeType()) {
+    case Node::kTextNode:
+    case Node::kAttributeNode:
+    case Node::kCommentNode:
+    case Node::kCdataSectionNode:
+    case Node::kDocumentFragmentNode:
+      node_value = node->nodeValue();
+      break;
+    default:
+      break;
+  }
+
+  int index = dom_tree_snapshot_->getNodeName(nullptr)->length();
+  int backend_node_id = DOMNodeIds::IdForNode(node);
+
+  // Create DOMNode object and add it to the result array before traversing
+  // children, so that parents appear before their children in the array.
+  dom_tree_snapshot_->getParentIndex(nullptr)->addItem(parent_index);
+
+  dom_tree_snapshot_->getNodeType(nullptr)->addItem(
+      static_cast<int>(node->getNodeType()));
+  dom_tree_snapshot_->getNodeName(nullptr)->addItem(
+      AddString(node->nodeName()));
+  dom_tree_snapshot_->getNodeValue(nullptr)->addItem(AddString(node_value));
+  dom_tree_snapshot_->getBackendNodeId(nullptr)->addItem(backend_node_id);
+  dom_tree_snapshot_->getAttributes(nullptr)->addItem(
+      BuildArrayForElementAttributes2(node));
+  dom_tree_snapshot_->getLayoutNodeIndex(nullptr)->addItem(
+      BuildLayoutTreeNode(node, index));
+
+  if (origin_url_map_ && origin_url_map_->Contains(backend_node_id)) {
+    String origin_url = origin_url_map_->at(backend_node_id);
+    // In common cases, it is implicit that a child node would have the same
+    // origin url as its parent, so no need to mark twice.
+    if (!node->parentNode() || origin_url_map_->at(DOMNodeIds::IdForNode(
+                                   node->parentNode())) != origin_url) {
+      SetRare(dom_tree_snapshot_->getOriginURL(nullptr), index, origin_url);
+    }
+  }
+
+  if (node->WillRespondToMouseClickEvents())
+    SetRare(dom_tree_snapshot_->getIsClickable(nullptr), index);
+
+  if (node->IsElementNode()) {
+    Element* element = ToElement(node);
+    if (node->IsFrameOwnerElement()) {
+      const HTMLFrameOwnerElement* frame_owner = ToHTMLFrameOwnerElement(node);
+      if (LocalFrame* frame =
+              frame_owner->ContentFrame() &&
+                      frame_owner->ContentFrame()->IsLocalFrame()
+                  ? ToLocalFrame(frame_owner->ContentFrame())
+                  : nullptr) {
+        SetRare(dom_tree_snapshot_->getFrameId(nullptr), index,
+                IdentifiersFactory::FrameId(frame));
+      }
+      if (Document* doc = frame_owner->contentDocument()) {
+        SetRare(dom_tree_snapshot_->getContentDocumentIndex(nullptr), index,
+                VisitNode2(doc, index));
+      }
+    }
+
+    if (node->parentNode() && node->parentNode()->IsDocumentNode()) {
+      LocalFrame* frame = node->GetDocument().GetFrame();
+      if (frame) {
+        SetRare(dom_tree_snapshot_->getFrameId(nullptr), index,
+                IdentifiersFactory::FrameId(frame));
+      }
+    }
+
+    if (auto* link_element = ToHTMLLinkElementOrNull(*element)) {
+      if (link_element->IsImport() && link_element->import() &&
+          InspectorDOMAgent::InnerParentNode(link_element->import()) ==
+              link_element) {
+        SetRare(dom_tree_snapshot_->getImportedDocumentIndex(nullptr), index,
+                VisitNode2(link_element->import(), index));
+      }
+    }
+
+    if (auto* template_element = ToHTMLTemplateElementOrNull(*element)) {
+      SetRare(dom_tree_snapshot_->getTemplateContentIndex(nullptr), index,
+              VisitNode2(template_element->content(), index));
+    }
+
+    if (auto* textarea_element = ToHTMLTextAreaElementOrNull(*element)) {
+      SetRare(dom_tree_snapshot_->getTextValue(nullptr), index,
+              textarea_element->value());
+    }
+
+    if (auto* input_element = ToHTMLInputElementOrNull(*element)) {
+      SetRare(dom_tree_snapshot_->getInputValue(nullptr), index,
+              input_element->value());
+      if ((input_element->type() == InputTypeNames::radio) ||
+          (input_element->type() == InputTypeNames::checkbox)) {
+        if (input_element->checked()) {
+          SetRare(dom_tree_snapshot_->getInputChecked(nullptr), index);
+        }
+      }
+    }
+
+    if (auto* option_element = ToHTMLOptionElementOrNull(*element)) {
+      if (option_element->Selected()) {
+        SetRare(dom_tree_snapshot_->getOptionSelected(nullptr), index);
+      }
+    }
+
+    if (element->GetPseudoId()) {
+      protocol::DOM::PseudoType pseudo_type;
+      if (InspectorDOMAgent::GetPseudoElementType(element->GetPseudoId(),
+                                                  &pseudo_type)) {
+        SetRare(dom_tree_snapshot_->getPseudoType(nullptr), index, pseudo_type);
+      }
+    } else {
+      VisitPseudoElements2(element, index);
+    }
+
+    HTMLImageElement* image_element = ToHTMLImageElementOrNull(node);
+    if (image_element) {
+      SetRare(dom_tree_snapshot_->getCurrentSourceURL(nullptr), index,
+              image_element->currentSrc());
+    }
+  } else if (node->IsDocumentNode()) {
+    Document* document = ToDocument(node);
+    SetRare(dom_tree_snapshot_->getDocumentURL(nullptr), index,
+            InspectorDOMAgent::DocumentURLString(document));
+    SetRare(dom_tree_snapshot_->getBaseURL(nullptr), index,
+            InspectorDOMAgent::DocumentBaseURLString(document));
+    if (document->ContentLanguage()) {
+      SetRare(dom_tree_snapshot_->getContentLanguage(nullptr), index,
+              document->ContentLanguage());
+    }
+    if (document->EncodingName()) {
+      SetRare(dom_tree_snapshot_->getDocumentEncoding(nullptr), index,
+              document->EncodingName());
+    }
+    SetRare(dom_tree_snapshot_->getFrameId(nullptr), index,
+            IdentifiersFactory::FrameId(document->GetFrame()));
+  } else if (node->IsDocumentTypeNode()) {
+    DocumentType* doc_type = ToDocumentType(node);
+    SetRare(dom_tree_snapshot_->getPublicId(nullptr), index,
+            doc_type->publicId());
+    SetRare(dom_tree_snapshot_->getSystemId(nullptr), index,
+            doc_type->systemId());
+  }
+
+  if (node->IsContainerNode())
+    VisitContainerChildren2(node, index);
+  return index;
+}
+
 Node* InspectorDOMSnapshotAgent::FirstChild(
     const Node& node,
     bool include_user_agent_shadow_tree) {
@@ -473,6 +775,17 @@
   return children;
 }
 
+void InspectorDOMSnapshotAgent::VisitContainerChildren2(Node* container,
+                                                        int parent_index) {
+  if (!HasChildren(*container, false))
+    return;
+
+  for (Node* child = FirstChild(*container, false); child;
+       child = NextSibling(*child, false)) {
+    VisitNode2(child, parent_index);
+  }
+}
+
 std::unique_ptr<protocol::Array<int>>
 InspectorDOMSnapshotAgent::VisitPseudoElements(
     Element* parent,
@@ -499,6 +812,21 @@
   return pseudo_elements;
 }
 
+void InspectorDOMSnapshotAgent::VisitPseudoElements2(Element* parent,
+                                                     int parent_index) {
+  if (!parent->GetPseudoElement(kPseudoIdBefore) &&
+      !parent->GetPseudoElement(kPseudoIdAfter)) {
+    return;
+  }
+
+  auto pseudo_elements = protocol::Array<int>::create();
+
+  if (parent->GetPseudoElement(kPseudoIdBefore))
+    VisitNode2(parent->GetPseudoElement(kPseudoIdBefore), parent_index);
+  if (parent->GetPseudoElement(kPseudoIdAfter))
+    VisitNode2(parent->GetPseudoElement(kPseudoIdAfter), parent_index);
+}
+
 std::unique_ptr<protocol::Array<protocol::DOMSnapshot::NameValue>>
 InspectorDOMSnapshotAgent::BuildArrayForElementAttributes(Element* element) {
   auto attributes_value =
@@ -515,6 +843,19 @@
   return attributes_value;
 }
 
+std::unique_ptr<protocol::Array<int>>
+InspectorDOMSnapshotAgent::BuildArrayForElementAttributes2(Node* node) {
+  auto result = protocol::Array<int>::create();
+  if (!node->IsElementNode())
+    return result;
+  AttributeCollection attributes = ToElement(node)->Attributes();
+  for (const auto& attribute : attributes) {
+    result->addItem(AddString(attribute.GetName().ToString()));
+    result->addItem(AddString(attribute.Value()));
+  }
+  return result;
+}
+
 int InspectorDOMSnapshotAgent::VisitLayoutTreeNode(Node* node, int node_index) {
   LayoutObject* layout_object = node->GetLayoutObject();
   if (!layout_object)
@@ -569,6 +910,44 @@
   return index;
 }
 
+int InspectorDOMSnapshotAgent::BuildLayoutTreeNode(Node* node, int node_index) {
+  LayoutObject* layout_object = node->GetLayoutObject();
+  if (!layout_object)
+    return -1;
+
+  int index = layout_tree_snapshot_->getNodeIndex()->length();
+  layout_tree_snapshot_->getNodeIndex()->addItem(node_index);
+  layout_tree_snapshot_->getStyles()->addItem(BuildStylesForNode(node));
+  layout_tree_snapshot_->getBounds()->addItem(
+      BuildRectForFloatRect2(layout_object->AbsoluteBoundingBoxRect()));
+  auto* text_box_snapshot = layout_tree_snapshot_->getTextBoxes();
+
+  String text = layout_object->IsText() ? ToLayoutText(layout_object)->GetText()
+                                        : String();
+  layout_tree_snapshot_->getText()->addItem(AddString(text));
+
+  if (!layout_object->IsText())
+    return index;
+
+  LayoutText* layout_text = ToLayoutText(layout_object);
+  if (!layout_text->HasTextBoxes())
+    return index;
+
+  for (const InlineTextBox* text_box : layout_text->TextBoxes()) {
+    FloatRect local_coords_text_box_rect(text_box->FrameRect());
+    FloatRect absolute_coords_text_box_rect =
+        layout_object->LocalToAbsoluteQuad(local_coords_text_box_rect)
+            .BoundingBox();
+    text_box_snapshot->getLayoutIndex()->addItem(index);
+    text_box_snapshot->getBounds()->addItem(
+        BuildRectForFloatRect2(absolute_coords_text_box_rect));
+    text_box_snapshot->getStart()->addItem(text_box->Start());
+    text_box_snapshot->getLength()->addItem(text_box->Len());
+  }
+
+  return index;
+}
+
 int InspectorDOMSnapshotAgent::GetStyleIndexForNode(Node* node) {
   CSSComputedStyleDeclaration* computed_style_info =
       CSSComputedStyleDeclaration::Create(node, true);
@@ -611,6 +990,18 @@
   return index;
 }
 
+std::unique_ptr<protocol::Array<int>>
+InspectorDOMSnapshotAgent::BuildStylesForNode(Node* node) {
+  CSSComputedStyleDeclaration* computed_style_info =
+      CSSComputedStyleDeclaration::Create(node, true);
+  std::unique_ptr<protocol::Array<int>> result = protocol::Array<int>::create();
+  for (const auto& pair : *css_property_whitelist_) {
+    String value = computed_style_info->GetPropertyValue(pair.second);
+    result->addItem(AddString(value));
+  }
+  return result;
+}
+
 void InspectorDOMSnapshotAgent::TraversePaintLayerTree(Document* document) {
   // Update layout tree before traversal of document so that we inspect a
   // current and consistent state of all trees.
diff --git a/third_party/blink/renderer/core/inspector/inspector_dom_snapshot_agent.h b/third_party/blink/renderer/core/inspector/inspector_dom_snapshot_agent.h
index 0d72223..8a54c415 100644
--- a/third_party/blink/renderer/core/inspector/inspector_dom_snapshot_agent.h
+++ b/third_party/blink/renderer/core/inspector/inspector_dom_snapshot_agent.h
@@ -49,6 +49,11 @@
           layout_tree_nodes,
       std::unique_ptr<protocol::Array<protocol::DOMSnapshot::ComputedStyle>>*
           computed_styles) override;
+  protocol::Response captureSnapshot(
+      std::unique_ptr<protocol::Array<String>> computed_styles,
+      std::unique_ptr<protocol::DOMSnapshot::DOMTreeSnapshot>* nodes,
+      std::unique_ptr<protocol::DOMSnapshot::LayoutTreeSnapshot>* layout,
+      std::unique_ptr<protocol::Array<String>>* strings) override;
 
   bool Enabled() const;
 
@@ -60,11 +65,20 @@
   InspectorDOMSnapshotAgent(InspectedFrames*, InspectorDOMDebuggerAgent*);
   void InnerEnable();
 
-  // Adds a DOMNode for the given Node to |dom_nodes_| and returns its index.
   int VisitNode(Node*,
                 bool include_event_listeners,
                 bool include_user_agent_shadow_tree);
 
+  int AddString(const String& string);
+  void SetRare(protocol::DOMSnapshot::RareIntegerData* data,
+               int index,
+               int value);
+  void SetRare(protocol::DOMSnapshot::RareStringData* data,
+               int index,
+               const String& value);
+  void SetRare(protocol::DOMSnapshot::RareBooleanData* data, int index);
+  int VisitNode2(Node*, int parent_index);
+
   // Helpers for VisitContainerChildren.
   static Node* FirstChild(const Node& node,
                           bool include_user_agent_shadow_tree);
@@ -77,23 +91,28 @@
       Node* container,
       bool include_event_listeners,
       bool include_user_agent_shadow_tree);
+  void VisitContainerChildren2(Node* container, int parent_index);
   std::unique_ptr<protocol::Array<int>> VisitPseudoElements(
       Element* parent,
       bool include_event_listeners,
       bool include_user_agent_shadow_tree);
+  void VisitPseudoElements2(Element* parent, int parent_index);
   std::unique_ptr<protocol::Array<protocol::DOMSnapshot::NameValue>>
   BuildArrayForElementAttributes(Element*);
+  std::unique_ptr<protocol::Array<int>> BuildArrayForElementAttributes2(Node*);
 
   // Adds a LayoutTreeNode for the LayoutObject of the given Node to
   // |layout_tree_nodes_| and returns its index. Returns -1 if the Node has no
   // associated LayoutObject.
   int VisitLayoutTreeNode(Node*, int node_index);
+  int BuildLayoutTreeNode(Node*, int node_index);
 
   // Returns the index of the ComputedStyle in |computed_styles_| for the given
   // Node. Adds a new ComputedStyle if necessary, but ensures no duplicates are
   // added to |computed_styles_|. Returns -1 if the node has no values for
   // styles in |style_whitelist_|.
   int GetStyleIndexForNode(Node*);
+  std::unique_ptr<protocol::Array<int>> BuildStylesForNode(Node*);
 
   // Traverses the PaintLayer tree in paint order to fill |paint_order_map_|.
   void TraversePaintLayerTree(Document*);
@@ -116,6 +135,14 @@
       layout_tree_nodes_;
   std::unique_ptr<protocol::Array<protocol::DOMSnapshot::ComputedStyle>>
       computed_styles_;
+
+  std::unique_ptr<protocol::Array<String>> strings_;
+  WTF::HashMap<String, int> string_table_;
+
+  std::unique_ptr<protocol::DOMSnapshot::DOMTreeSnapshot> dom_tree_snapshot_;
+  std::unique_ptr<protocol::DOMSnapshot::LayoutTreeSnapshot>
+      layout_tree_snapshot_;
+
   // Maps a style string vector to an index in |computed_styles_|. Used to avoid
   // duplicate entries in |computed_styles_|.
   std::unique_ptr<ComputedStylesMap> computed_styles_map_;
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 351ea2d..13cbc84 100644
--- a/third_party/blink/renderer/core/inspector/inspector_overlay_agent.cc
+++ b/third_party/blink/renderer/core/inspector/inspector_overlay_agent.cc
@@ -97,9 +97,10 @@
   if (ignore_pointer_events_none)
     hit_type |= HitTestRequest::kIgnorePointerEventsNone;
   HitTestRequest request(hit_type);
-  HitTestResult result(
-      request, frame->View()->ConvertFromRootFrame(point_in_root_frame));
-  frame->ContentLayoutObject()->HitTest(result);
+  HitTestLocation location(
+      frame->View()->ConvertFromRootFrame(point_in_root_frame));
+  HitTestResult result(request, location);
+  frame->ContentLayoutObject()->HitTest(location, result);
   Node* node = result.InnerPossiblyPseudoNode();
   while (node && node->getNodeType() == Node::kTextNode)
     node = node->parentNode();
diff --git a/third_party/blink/renderer/core/layout/grid_track_sizing_algorithm.cc b/third_party/blink/renderer/core/layout/grid_track_sizing_algorithm.cc
index 3a2a4e92..1c48f5ea 100644
--- a/third_party/blink/renderer/core/layout/grid_track_sizing_algorithm.cc
+++ b/third_party/blink/renderer/core/layout/grid_track_sizing_algorithm.cc
@@ -826,8 +826,10 @@
   // If the logical width/height of the grid container is indefinite, percentage
   // values are treated as <auto>.
   if (IsRelativeSizedTrackAsAuto(track_size, direction)) {
-    UseCounter::Count(layout_grid_->GetDocument(),
-                      WebFeature::kGridRowTrackPercentIndefiniteHeight);
+    if (direction == kForRows) {
+      UseCounter::Count(layout_grid_->GetDocument(),
+                        WebFeature::kGridRowTrackPercentIndefiniteHeight);
+    }
     if (min_track_breadth.HasPercentage())
       min_track_breadth = Length(kAuto);
     if (max_track_breadth.HasPercentage())
diff --git a/third_party/blink/renderer/core/layout/hit_test_cache.cc b/third_party/blink/renderer/core/layout/hit_test_cache.cc
index 2d31dde..5b650e1e 100644
--- a/third_party/blink/renderer/core/layout/hit_test_cache.cc
+++ b/third_party/blink/renderer/core/layout/hit_test_cache.cc
@@ -9,7 +9,8 @@
 
 namespace blink {
 
-bool HitTestCache::LookupCachedResult(HitTestResult& hit_result,
+bool HitTestCache::LookupCachedResult(const HitTestLocation& location,
+                                      HitTestResult& hit_result,
                                       uint64_t dom_tree_version) {
   bool result = false;
   HitHistogramMetric metric = HitHistogramMetric::MISS;
@@ -17,15 +18,14 @@
     metric = HitHistogramMetric::MISS_EXPLICIT_AVOID;
     // For now we don't support rect based hit results.
   } else if (dom_tree_version == dom_tree_version_ &&
-             !hit_result.GetHitTestLocation().IsRectBasedTest()) {
+             !location.IsRectBasedTest()) {
     for (const auto& cached_item : items_) {
-      if (cached_item.GetHitTestLocation().Point() ==
-          hit_result.GetHitTestLocation().Point()) {
+      if (cached_item.location.Point() == location.Point()) {
         if (hit_result.GetHitTestRequest().EqualForCacheability(
-                cached_item.GetHitTestRequest())) {
+                cached_item.result.GetHitTestRequest())) {
           metric = HitHistogramMetric::HIT_EXACT_MATCH;
           result = true;
-          hit_result = cached_item;
+          hit_result = cached_item.result;
           break;
         }
         metric = HitHistogramMetric::MISS_VALIDITY_RECT_MATCHES;
@@ -40,7 +40,17 @@
   return result;
 }
 
-void HitTestCache::AddCachedResult(const HitTestResult& result,
+void HitTestCacheEntry::Trace(blink::Visitor* visitor) {
+  visitor->Trace(result);
+}
+
+void HitTestCacheEntry::CacheValues(const HitTestCacheEntry& other) {
+  *this = other;
+  result.CacheValues(other.result);
+}
+
+void HitTestCache::AddCachedResult(const HitTestLocation& location,
+                                   const HitTestResult& result,
                                    uint64_t dom_tree_version) {
   if (!result.IsCacheable())
     return;
@@ -52,15 +62,17 @@
     return;
 
   // For now don't support rect based or list based requests.
-  if (result.GetHitTestLocation().IsRectBasedTest() ||
-      result.GetHitTestRequest().ListBased())
+  if (location.IsRectBasedTest() || result.GetHitTestRequest().ListBased())
     return;
   if (dom_tree_version != dom_tree_version_)
     Clear();
   if (items_.size() < HIT_TEST_CACHE_SIZE)
     items_.resize(update_index_ + 1);
 
-  items_.at(update_index_).CacheValues(result);
+  HitTestCacheEntry cache_entry;
+  cache_entry.location = location;
+  cache_entry.result = result;
+  items_.at(update_index_).CacheValues(cache_entry);
   dom_tree_version_ = dom_tree_version;
 
   update_index_++;
diff --git a/third_party/blink/renderer/core/layout/hit_test_cache.h b/third_party/blink/renderer/core/layout/hit_test_cache.h
index c01a9315..6ffdf841 100644
--- a/third_party/blink/renderer/core/layout/hit_test_cache.h
+++ b/third_party/blink/renderer/core/layout/hit_test_cache.h
@@ -34,6 +34,16 @@
 // size of 1.
 #define HIT_TEST_CACHE_SIZE (2)
 
+struct HitTestCacheEntry {
+  DISALLOW_NEW_EXCEPT_PLACEMENT_NEW();
+
+  void Trace(blink::Visitor*);
+  HitTestLocation location;
+  HitTestResult result;
+
+  void CacheValues(const HitTestCacheEntry&);
+};
+
 class CORE_EXPORT HitTestCache final
     : public GarbageCollectedFinalized<HitTestCache> {
  public:
@@ -41,12 +51,16 @@
 
   // Check the cache for a possible hit and update |result| if
   // hit encountered; returning true. Otherwise false.
-  bool LookupCachedResult(HitTestResult&, uint64_t dom_tree_version);
+  bool LookupCachedResult(const HitTestLocation&,
+                          HitTestResult&,
+                          uint64_t dom_tree_version);
 
   void Clear();
 
   // Adds a HitTestResult to the cache.
-  void AddCachedResult(const HitTestResult&, uint64_t dom_tree_version);
+  void AddCachedResult(const HitTestLocation&,
+                       const HitTestResult&,
+                       uint64_t dom_tree_version);
 
   void Trace(blink::Visitor*);
 
@@ -70,11 +84,14 @@
   };
 
   unsigned update_index_;
-  HeapVector<HitTestResult, HIT_TEST_CACHE_SIZE> items_;
+
+  HeapVector<HitTestCacheEntry, HIT_TEST_CACHE_SIZE> items_;
   uint64_t dom_tree_version_;
   DISALLOW_COPY_AND_ASSIGN(HitTestCache);
 };
 
 }  // namespace blink
 
+WTF_ALLOW_CLEAR_UNUSED_SLOTS_WITH_MEM_FUNCTIONS(blink::HitTestCacheEntry);
+
 #endif  // THIRD_PARTY_BLINK_RENDERER_CORE_LAYOUT_HIT_TEST_CACHE_H_
diff --git a/third_party/blink/renderer/core/layout/hit_test_location.cc b/third_party/blink/renderer/core/layout/hit_test_location.cc
index 869f346..8d795de 100644
--- a/third_party/blink/renderer/core/layout/hit_test_location.cc
+++ b/third_party/blink/renderer/core/layout/hit_test_location.cc
@@ -28,6 +28,9 @@
 HitTestLocation::HitTestLocation()
     : is_rect_based_(false), is_rectilinear_(true) {}
 
+HitTestLocation::HitTestLocation(const IntPoint& point)
+    : HitTestLocation(LayoutPoint(point)) {}
+
 HitTestLocation::HitTestLocation(const LayoutPoint& point)
     : point_(point),
       bounding_box_(RectForPoint(point)),
@@ -44,6 +47,9 @@
       is_rect_based_(false),
       is_rectilinear_(true) {}
 
+HitTestLocation::HitTestLocation(const DoublePoint& point)
+    : HitTestLocation(FloatPoint(point)) {}
+
 HitTestLocation::HitTestLocation(const FloatPoint& point, const FloatQuad& quad)
     : transformed_point_(point), transformed_rect_(quad), is_rect_based_(true) {
   point_ = FlooredLayoutPoint(point);
diff --git a/third_party/blink/renderer/core/layout/hit_test_location.h b/third_party/blink/renderer/core/layout/hit_test_location.h
index eee1949..26704ce 100644
--- a/third_party/blink/renderer/core/layout/hit_test_location.h
+++ b/third_party/blink/renderer/core/layout/hit_test_location.h
@@ -41,10 +41,12 @@
   // Note that all points are in contents (aka "page") coordinate space for the
   // document that is being hit tested.
   HitTestLocation();
-  HitTestLocation(const LayoutPoint&);
-  HitTestLocation(const FloatPoint&);
-  HitTestLocation(const FloatPoint&, const FloatQuad&);
-  HitTestLocation(const LayoutRect&);
+  explicit HitTestLocation(const LayoutPoint&);
+  explicit HitTestLocation(const IntPoint&);
+  explicit HitTestLocation(const FloatPoint&);
+  explicit HitTestLocation(const DoublePoint&);
+  explicit HitTestLocation(const FloatPoint&, const FloatQuad&);
+  explicit HitTestLocation(const LayoutRect&);
   HitTestLocation(const HitTestLocation&, const LayoutSize& offset);
   HitTestLocation(const HitTestLocation&);
   ~HitTestLocation();
diff --git a/third_party/blink/renderer/core/layout/hit_test_result.cc b/third_party/blink/renderer/core/layout/hit_test_result.cc
index 4e8afb7..83fcfdd 100644
--- a/third_party/blink/renderer/core/layout/hit_test_result.cc
+++ b/third_party/blink/renderer/core/layout/hit_test_result.cc
@@ -50,35 +50,19 @@
 HitTestResult::HitTestResult()
     : hit_test_request_(HitTestRequest::kReadOnly | HitTestRequest::kActive),
       cacheable_(true),
-      is_over_embedded_content_view_(false) {}
-
-HitTestResult::HitTestResult(const HitTestRequest& request,
-                             const LayoutPoint& point)
-    : hit_test_location_(point),
-      hit_test_request_(request),
-      cacheable_(true),
-      point_in_inner_node_frame_(point),
-      is_over_embedded_content_view_(false) {}
-
-HitTestResult::HitTestResult(const HitTestRequest& request,
-                             const LayoutRect& rect)
-    : hit_test_location_(rect),
-      hit_test_request_(request),
-      cacheable_(true),
-      point_in_inner_node_frame_(rect.Center()),
-      is_over_embedded_content_view_(false) {}
+      is_over_embedded_content_view_(false),
+      is_rect_based_test_(false) {}
 
 HitTestResult::HitTestResult(const HitTestRequest& other_request,
-                             const HitTestLocation& other)
-    : hit_test_location_(other),
-      hit_test_request_(other_request),
+                             const HitTestLocation& location)
+    : hit_test_request_(other_request),
       cacheable_(true),
-      point_in_inner_node_frame_(hit_test_location_.Point()),
-      is_over_embedded_content_view_(false) {}
+      point_in_inner_node_frame_(location.Point()),
+      is_over_embedded_content_view_(false),
+      is_rect_based_test_(location.IsRectBasedTest()) {}
 
 HitTestResult::HitTestResult(const HitTestResult& other)
-    : hit_test_location_(other.hit_test_location_),
-      hit_test_request_(other.hit_test_request_),
+    : hit_test_request_(other.hit_test_request_),
       cacheable_(other.cacheable_),
       inner_node_(other.InnerNode()),
       inner_possibly_pseudo_node_(other.inner_possibly_pseudo_node_),
@@ -87,7 +71,8 @@
       inner_url_element_(other.URLElement()),
       scrollbar_(other.GetScrollbar()),
       is_over_embedded_content_view_(other.IsOverEmbeddedContentView()),
-      canvas_region_id_(other.CanvasRegionId()) {
+      canvas_region_id_(other.CanvasRegionId()),
+      is_rect_based_test_(other.IsRectBasedTest()) {
   // Only copy the NodeSet in case of list hit test.
   list_based_test_result_ = other.list_based_test_result_
                                 ? new NodeSet(*other.list_based_test_result_)
@@ -97,7 +82,6 @@
 HitTestResult::~HitTestResult() = default;
 
 HitTestResult& HitTestResult::operator=(const HitTestResult& other) {
-  hit_test_location_ = other.hit_test_location_;
   hit_test_request_ = other.hit_test_request_;
   PopulateFromCachedResult(other);
 
@@ -116,7 +100,6 @@
 }
 
 void HitTestResult::CacheValues(const HitTestResult& other) {
-  *this = other;
   hit_test_request_ =
       other.hit_test_request_.GetType() & ~HitTestRequest::kAvoidCache;
 }
@@ -131,6 +114,7 @@
   is_over_embedded_content_view_ = other.IsOverEmbeddedContentView();
   cacheable_ = other.cacheable_;
   canvas_region_id_ = other.CanvasRegionId();
+  is_rect_based_test_ = other.IsRectBasedTest();
 
   // Only copy the NodeSet in case of list hit test.
   list_based_test_result_ = other.list_based_test_result_
@@ -236,12 +220,12 @@
   return nullptr;
 }
 
-bool HitTestResult::IsSelected() const {
+bool HitTestResult::IsSelected(const HitTestLocation& location) const {
   if (!inner_node_)
     return false;
 
   if (LocalFrame* frame = inner_node_->GetDocument().GetFrame())
-    return frame->Selection().Contains(hit_test_location_.Point());
+    return frame->Selection().Contains(location.Point());
   return false;
 }
 
@@ -468,13 +452,10 @@
   return *list_based_test_result_;
 }
 
-void HitTestResult::ResolveRectBasedTest(
+HitTestLocation HitTestResult::ResolveRectBasedTest(
     Node* resolved_inner_node,
     const LayoutPoint& resolved_point_in_main_frame) {
   DCHECK(IsRectBasedTest());
-  DCHECK(hit_test_location_.ContainsPoint(
-      FloatPoint(resolved_point_in_main_frame)));
-  hit_test_location_ = HitTestLocation(resolved_point_in_main_frame);
   point_in_inner_node_frame_ = resolved_point_in_main_frame;
   inner_node_ = nullptr;
   inner_possibly_pseudo_node_ = nullptr;
@@ -487,7 +468,9 @@
   DCHECK(resolved_inner_node);
   if (auto* layout_object = resolved_inner_node->GetLayoutObject())
     layout_object->UpdateHitTestResult(*this, LayoutPoint());
-  DCHECK(!IsRectBasedTest());
+  is_rect_based_test_ = false;
+
+  return HitTestLocation(resolved_point_in_main_frame);
 }
 
 Element* HitTestResult::InnerElement() const {
diff --git a/third_party/blink/renderer/core/layout/hit_test_result.h b/third_party/blink/renderer/core/layout/hit_test_result.h
index d5ecd72..72147e5 100644
--- a/third_party/blink/renderer/core/layout/hit_test_result.h
+++ b/third_party/blink/renderer/core/layout/hit_test_result.h
@@ -59,8 +59,6 @@
   typedef HeapListHashSet<Member<Node>> NodeSet;
 
   HitTestResult();
-  HitTestResult(const HitTestRequest&, const LayoutPoint&);
-  HitTestResult(const HitTestRequest&, const LayoutRect&);
   HitTestResult(const HitTestRequest&, const HitTestLocation&);
   HitTestResult(const HitTestResult&);
   ~HitTestResult();
@@ -68,7 +66,7 @@
   void Trace(blink::Visitor*);
 
   bool EqualForCacheability(const HitTestResult&) const;
-  void CacheValues(const HitTestResult&);
+  void CacheValues(const HitTestResult& other);
 
   // Populate this object based on another HitTestResult; similar to assignment
   // operator but don't assign any of the request parameters. ie. This method
@@ -97,15 +95,7 @@
   }
 
   // Forwarded from HitTestLocation
-  bool IsRectBasedTest() const { return hit_test_location_.IsRectBasedTest(); }
-
-  // The hit-tested point in the coordinates of the main frame.
-  const LayoutPoint& PointInMainFrame() const {
-    return hit_test_location_.Point();
-  }
-  IntPoint RoundedPointInMainFrame() const {
-    return RoundedIntPoint(PointInMainFrame());
-  }
+  bool IsRectBasedTest() const { return is_rect_based_test_; }
 
   // The hit-tested point in the coordinates of the innerNode frame, the frame
   // containing innerNode.
@@ -132,9 +122,6 @@
 
   void SetToShadowHostIfInRestrictedShadowRoot();
 
-  const HitTestLocation& GetHitTestLocation() const {
-    return hit_test_location_;
-  }
   const HitTestRequest& GetHitTestRequest() const { return hit_test_request_; }
 
   void SetInnerNode(Node*);
@@ -145,7 +132,7 @@
     is_over_embedded_content_view_ = b;
   }
 
-  bool IsSelected() const;
+  bool IsSelected(const HitTestLocation& location) const;
   String Title(TextDirection&) const;
   const AtomicString& AltDisplayString() const;
   Image* GetImage() const;
@@ -188,14 +175,14 @@
 
   // Collapse the rect-based test result into a single target at the specified
   // location.
-  void ResolveRectBasedTest(Node* resolved_inner_node,
-                            const LayoutPoint& resolved_point_in_main_frame);
+  HitTestLocation ResolveRectBasedTest(
+      Node* resolved_inner_node,
+      const LayoutPoint& resolved_point_in_main_frame);
 
  private:
   NodeSet& MutableListBasedTestResult();  // See above.
   HTMLMediaElement* MediaElement() const;
 
-  HitTestLocation hit_test_location_;
   HitTestRequest hit_test_request_;
   bool cacheable_;
 
@@ -217,6 +204,7 @@
 
   mutable Member<NodeSet> list_based_test_result_;
   String canvas_region_id_;
+  bool is_rect_based_test_;
 };
 
 }  // namespace blink
diff --git a/third_party/blink/renderer/core/layout/layout_box_model_object_test.cc b/third_party/blink/renderer/core/layout/layout_box_model_object_test.cc
index ccd8a3b6..51ff438c 100644
--- a/third_party/blink/renderer/core/layout/layout_box_model_object_test.cc
+++ b/third_party/blink/renderer/core/layout/layout_box_model_object_test.cc
@@ -165,7 +165,8 @@
   EXPECT_EQ(
       IntRect(0, 100, 10, 10),
       EnclosingIntRect(GetScrollContainerRelativeStickyBoxRect(constraints)));
-  EXPECT_EQ(IntRect(0, 50, 100, 100), sticky->ComputeStickyConstrainingRect());
+  EXPECT_EQ(IntRect(0, 50, 100, 100),
+            EnclosingIntRect(sticky->ComputeStickyConstrainingRect()));
 }
 
 // Verifies that the sticky constraints are correctly computed for sticky with
@@ -224,7 +225,7 @@
       IntRect(2190, 100, 10, 10),
       EnclosingIntRect(GetScrollContainerRelativeStickyBoxRect(constraints)));
   EXPECT_EQ(IntRect(2100, 50, 100, 100),
-            sticky->ComputeStickyConstrainingRect());
+            EnclosingIntRect(sticky->ComputeStickyConstrainingRect()));
 }
 
 // Verifies that the sticky constraints are not affected by transforms
diff --git a/third_party/blink/renderer/core/layout/layout_embedded_content.cc b/third_party/blink/renderer/core/layout/layout_embedded_content.cc
index 2d6e7cc9..131aa0f 100644
--- a/third_party/blink/renderer/core/layout/layout_embedded_content.cc
+++ b/third_party/blink/renderer/core/layout/layout_embedded_content.cc
@@ -202,8 +202,8 @@
                                        new_hit_test_location);
 
       // The frame's layout and style must be up to date if we reach here.
-      bool is_inside_child_frame =
-          child_layout_view->HitTestNoLifecycleUpdate(child_frame_result);
+      bool is_inside_child_frame = child_layout_view->HitTestNoLifecycleUpdate(
+          new_hit_test_location, child_frame_result);
 
       if (result.GetHitTestRequest().ListBased()) {
         result.Append(child_frame_result);
diff --git a/third_party/blink/renderer/core/layout/layout_image.cc b/third_party/blink/renderer/core/layout/layout_image.cc
index 652aff359..9f449a30 100644
--- a/third_party/blink/renderer/core/layout/layout_image.cc
+++ b/third_party/blink/renderer/core/layout/layout_image.cc
@@ -353,8 +353,7 @@
                               const HitTestLocation& location_in_container,
                               const LayoutPoint& accumulated_offset,
                               HitTestAction hit_test_action) {
-  HitTestResult temp_result(result.GetHitTestRequest(),
-                            result.GetHitTestLocation());
+  HitTestResult temp_result(result.GetHitTestRequest(), location_in_container);
   bool inside = LayoutReplaced::NodeAtPoint(
       temp_result, location_in_container, accumulated_offset, hit_test_action);
 
diff --git a/third_party/blink/renderer/core/layout/layout_inline_test.cc b/third_party/blink/renderer/core/layout/layout_inline_test.cc
index 0c29bf6..ea46a2b 100644
--- a/third_party/blink/renderer/core/layout/layout_inline_test.cc
+++ b/third_party/blink/renderer/core/layout/layout_inline_test.cc
@@ -107,12 +107,14 @@
 
   HitTestRequest hit_request(HitTestRequest::kTouchEvent |
                              HitTestRequest::kListBased);
+
   LayoutRect hit_rect(1, 3, 2, 4);
-  HitTestResult hit_result(hit_request, hit_rect);
+  HitTestLocation location(hit_rect);
+  HitTestResult hit_result(hit_request, location);
   LayoutPoint hit_offset;
 
-  bool hit_outcome = lots_of_boxes->HitTestCulledInline(
-      hit_result, hit_result.GetHitTestLocation(), hit_offset);
+  bool hit_outcome =
+      lots_of_boxes->HitTestCulledInline(hit_result, location, hit_offset);
   // Assert checks that we both hit something and that the area covered
   // by "something" totally contains the hit region.
   EXPECT_TRUE(hit_outcome);
@@ -130,6 +132,7 @@
                              HitTestRequest::kActive);
   const LayoutPoint container_offset(8, 8);
   const LayoutPoint hit_location(18, 15);
+  HitTestLocation location(hit_location);
 
   Element* div = GetDocument().QuerySelector("div");
   Element* span = GetDocument().QuerySelector("span");
@@ -138,9 +141,9 @@
   // Shouldn't hit anything in SPAN as it's in another paint layer
   {
     LayoutObject* layout_div = div->GetLayoutObject();
-    HitTestResult hit_result(hit_request, hit_location);
-    bool hit_outcome = layout_div->HitTestAllPhases(hit_result, hit_location,
-                                                    container_offset);
+    HitTestResult hit_result(hit_request, location);
+    bool hit_outcome =
+        layout_div->HitTestAllPhases(hit_result, location, container_offset);
     EXPECT_TRUE(hit_outcome);
     EXPECT_EQ(div, hit_result.InnerNode());
   }
@@ -149,17 +152,17 @@
   // the SPAN itself.
   {
     LayoutObject* layout_span = span->GetLayoutObject();
-    HitTestResult hit_result(hit_request, hit_location);
-    bool hit_outcome = layout_span->HitTestAllPhases(hit_result, hit_location,
-                                                     container_offset);
+    HitTestResult hit_result(hit_request, location);
+    bool hit_outcome =
+        layout_span->HitTestAllPhases(hit_result, location, container_offset);
     EXPECT_TRUE(hit_outcome);
     EXPECT_EQ(text, hit_result.InnerNode());
   }
 
   // Hit test from LayoutView to verify that everything works together.
   {
-    HitTestResult hit_result(hit_request, hit_location);
-    bool hit_outcome = GetLayoutView().HitTest(hit_result);
+    HitTestResult hit_result(hit_request, location);
+    bool hit_outcome = GetLayoutView().HitTest(location, hit_result);
     EXPECT_TRUE(hit_outcome);
     EXPECT_EQ(text, hit_result.InnerNode());
   }
@@ -185,18 +188,19 @@
   // Hit test first line
   {
     LayoutPoint hit_location(13, 13);
+    HitTestLocation location(hit_location);
     Node* target = GetElementById("span")->firstChild();
 
-    HitTestResult hit_result(hit_request, hit_location);
-    bool hit_outcome = layout_span->HitTestAllPhases(hit_result, hit_location,
-                                                     container_offset);
+    HitTestResult hit_result(hit_request, location);
+    bool hit_outcome =
+        layout_span->HitTestAllPhases(hit_result, location, container_offset);
     EXPECT_TRUE(hit_outcome);
     EXPECT_EQ(target, hit_result.InnerNode());
 
     // Initiate a hit test from LayoutView to verify the "natural" process.
-    HitTestResult layout_view_hit_result(hit_request, hit_location);
+    HitTestResult layout_view_hit_result(hit_request, location);
     bool layout_view_hit_outcome =
-        GetLayoutView().HitTest(layout_view_hit_result);
+        GetLayoutView().HitTest(location, layout_view_hit_result);
     EXPECT_TRUE(layout_view_hit_outcome);
     EXPECT_EQ(target, layout_view_hit_result.InnerNode());
   }
@@ -204,18 +208,19 @@
   // Hit test second line
   {
     LayoutPoint hit_location(13, 23);
+    HitTestLocation location(hit_location);
     Node* target = GetElementById("line2")->firstChild();
 
-    HitTestResult hit_result(hit_request, hit_location);
-    bool hit_outcome = layout_span->HitTestAllPhases(hit_result, hit_location,
-                                                     container_offset);
+    HitTestResult hit_result(hit_request, location);
+    bool hit_outcome =
+        layout_span->HitTestAllPhases(hit_result, location, container_offset);
     EXPECT_TRUE(hit_outcome);
     EXPECT_EQ(target, hit_result.InnerNode());
 
     // Initiate a hit test from LayoutView to verify the "natural" process.
-    HitTestResult layout_view_hit_result(hit_request, hit_location);
+    HitTestResult layout_view_hit_result(hit_request, location);
     bool layout_view_hit_outcome =
-        GetLayoutView().HitTest(layout_view_hit_result);
+        GetLayoutView().HitTest(location, layout_view_hit_result);
     EXPECT_TRUE(layout_view_hit_outcome);
     EXPECT_EQ(target, layout_view_hit_result.InnerNode());
   }
@@ -223,18 +228,19 @@
   // Hit test image in third line
   {
     LayoutPoint hit_location(13, 33);
+    HitTestLocation location(hit_location);
     Node* target = GetDocument().QuerySelector("img");
 
-    HitTestResult hit_result(hit_request, hit_location);
-    bool hit_outcome = layout_span->HitTestAllPhases(hit_result, hit_location,
-                                                     container_offset);
+    HitTestResult hit_result(hit_request, location);
+    bool hit_outcome =
+        layout_span->HitTestAllPhases(hit_result, location, container_offset);
     EXPECT_TRUE(hit_outcome);
     EXPECT_EQ(target, hit_result.InnerNode());
 
     // Initiate a hit test from LayoutView to verify the "natural" process.
-    HitTestResult layout_view_hit_result(hit_request, hit_location);
+    HitTestResult layout_view_hit_result(hit_request, location);
     bool layout_view_hit_outcome =
-        GetLayoutView().HitTest(layout_view_hit_result);
+        GetLayoutView().HitTest(location, layout_view_hit_result);
     EXPECT_TRUE(layout_view_hit_outcome);
     EXPECT_EQ(target, layout_view_hit_result.InnerNode());
   }
diff --git a/third_party/blink/renderer/core/layout/layout_object.cc b/third_party/blink/renderer/core/layout/layout_object.cc
index 245e75b..66ee97d 100644
--- a/third_party/blink/renderer/core/layout/layout_object.cc
+++ b/third_party/blink/renderer/core/layout/layout_object.cc
@@ -1687,8 +1687,9 @@
       HitTestRequest::kListBased | HitTestRequest::kPenetratingList |
       HitTestRequest::kIgnorePointerEventsNone | HitTestRequest::kReadOnly |
       HitTestRequest::kIgnoreClipping;
-  return frame->GetEventHandler().HitTestResultAtRect(hit_rect, hit_type, this,
-                                                      true);
+  HitTestLocation location(hit_rect);
+  return frame->GetEventHandler().HitTestResultAtLocation(location, hit_type,
+                                                          this, true);
 }
 
 void LayoutObject::DirtyLinesFromChangedChild(LayoutObject*, MarkingBehavior) {}
diff --git a/third_party/blink/renderer/core/layout/layout_view.cc b/third_party/blink/renderer/core/layout/layout_view.cc
index 14c78e51..e86a0b1 100644
--- a/third_party/blink/renderer/core/layout/layout_view.cc
+++ b/third_party/blink/renderer/core/layout/layout_view.cc
@@ -115,7 +115,8 @@
 
 LayoutView::~LayoutView() = default;
 
-bool LayoutView::HitTest(HitTestResult& result) {
+bool LayoutView::HitTest(const HitTestLocation& location,
+                         HitTestResult& result) {
   // We have to recursively update layout/style here because otherwise, when the
   // hit test recurses into a child document, it could trigger a layout on the
   // parent document, which can destroy PaintLayer that are higher up in the
@@ -128,25 +129,26 @@
     return false;
   HitTestLatencyRecorder hit_test_latency_recorder(
       result.GetHitTestRequest().AllowsChildFrameContent());
-  return HitTestNoLifecycleUpdate(result);
+  return HitTestNoLifecycleUpdate(location, result);
 }
 
-bool LayoutView::HitTestNoLifecycleUpdate(HitTestResult& result) {
+bool LayoutView::HitTestNoLifecycleUpdate(const HitTestLocation& location,
+                                          HitTestResult& result) {
   TRACE_EVENT_BEGIN0("blink,devtools.timeline", "HitTest");
   hit_test_count_++;
 
-  DCHECK(!result.GetHitTestLocation().IsRectBasedTest() ||
-         result.GetHitTestRequest().ListBased());
+  DCHECK(!location.IsRectBasedTest() || result.GetHitTestRequest().ListBased());
 
   uint64_t dom_tree_version = GetDocument().DomTreeVersion();
   HitTestResult cache_result = result;
   bool hit_layer = false;
-  if (hit_test_cache_->LookupCachedResult(cache_result, dom_tree_version)) {
+  if (hit_test_cache_->LookupCachedResult(location, cache_result,
+                                          dom_tree_version)) {
     hit_test_cache_hits_++;
     hit_layer = true;
     result = cache_result;
   } else {
-    hit_layer = Layer()->HitTest(result);
+    hit_layer = Layer()->HitTest(location, result);
 
     // If hitTestResult include scrollbar, innerNode should be the parent of the
     // scrollbar.
@@ -173,13 +175,12 @@
     }
 
     if (hit_layer)
-      hit_test_cache_->AddCachedResult(result, dom_tree_version);
+      hit_test_cache_->AddCachedResult(location, result, dom_tree_version);
   }
 
-  TRACE_EVENT_END1(
-      "blink,devtools.timeline", "HitTest", "endData",
-      InspectorHitTestEvent::EndData(result.GetHitTestRequest(),
-                                     result.GetHitTestLocation(), result));
+  TRACE_EVENT_END1("blink,devtools.timeline", "HitTest", "endData",
+                   InspectorHitTestEvent::EndData(result.GetHitTestRequest(),
+                                                  location, result));
   return hit_layer;
 }
 
diff --git a/third_party/blink/renderer/core/layout/layout_view.h b/third_party/blink/renderer/core/layout/layout_view.h
index 4a5d1e9..fed51c0 100644
--- a/third_party/blink/renderer/core/layout/layout_view.h
+++ b/third_party/blink/renderer/core/layout/layout_view.h
@@ -63,8 +63,9 @@
 
   // hitTest() will update layout, style and compositing first while
   // hitTestNoLifecycleUpdate() does not.
-  bool HitTest(HitTestResult&);
-  bool HitTestNoLifecycleUpdate(HitTestResult&);
+  bool HitTest(const HitTestLocation& location, HitTestResult&);
+  bool HitTestNoLifecycleUpdate(const HitTestLocation& location,
+                                HitTestResult&);
 
   // Returns the total count of calls to HitTest, for testing.
   unsigned HitTestCount() const { return hit_test_count_; }
diff --git a/third_party/blink/renderer/core/layout/shapes/shape.cc b/third_party/blink/renderer/core/layout/shapes/shape.cc
index cf7b7c2..717fbfa2 100644
--- a/third_party/blink/renderer/core/layout/shapes/shape.cc
+++ b/third_party/blink/renderer/core/layout/shapes/shape.cc
@@ -253,7 +253,7 @@
   // for layout, which is not allowed. See https://crbug.com/429346
   ImageObserverDisabler disabler(image);
   PaintFlags flags;
-  IntRect image_source_rect(IntPoint(), image->Size());
+  FloatRect image_source_rect(FloatPoint(), FloatSize(image->Size()));
   IntRect image_dest_rect(IntPoint(), image_size);
   // TODO(ccameron): No color conversion is required here.
   std::unique_ptr<cc::PaintCanvas> canvas =
diff --git a/third_party/blink/renderer/core/layout/svg/layout_svg_container.cc b/third_party/blink/renderer/core/layout/svg/layout_svg_container.cc
index d6b877e..3dda0b8 100644
--- a/third_party/blink/renderer/core/layout/svg/layout_svg_container.cc
+++ b/third_party/blink/renderer/core/layout/svg/layout_svg_container.cc
@@ -186,8 +186,9 @@
     if (child->NodeAtFloatPoint(result, local_point, hit_test_action)) {
       const LayoutPoint& local_layout_point = LayoutPoint(local_point);
       UpdateHitTestResult(result, local_layout_point);
-      if (result.AddNodeToListBasedTestResult(
-              child->GetNode(), local_layout_point) == kStopHitTesting)
+      HitTestLocation location(local_layout_point);
+      if (result.AddNodeToListBasedTestResult(child->GetNode(), location) ==
+          kStopHitTesting)
         return true;
     }
   }
@@ -201,8 +202,9 @@
         ObjectBoundingBox().Contains(local_point)) {
       const LayoutPoint& local_layout_point = LayoutPoint(local_point);
       UpdateHitTestResult(result, local_layout_point);
-      if (result.AddNodeToListBasedTestResult(
-              GetElement(), local_layout_point) == kStopHitTesting)
+      HitTestLocation location(local_layout_point);
+      if (result.AddNodeToListBasedTestResult(GetElement(), location) ==
+          kStopHitTesting)
         return true;
     }
   }
diff --git a/third_party/blink/renderer/core/layout/svg/layout_svg_foreign_object.cc b/third_party/blink/renderer/core/layout/svg/layout_svg_foreign_object.cc
index df0ba7d..86fbeeaa 100644
--- a/third_party/blink/renderer/core/layout/svg/layout_svg_foreign_object.cc
+++ b/third_party/blink/renderer/core/layout/svg/layout_svg_foreign_object.cc
@@ -136,9 +136,9 @@
   // |local_point| already includes the offset of the <foreignObject> element,
   // but PaintLayer::HitTestLayer assumes it has not been.
   point_in_foreign_object.MoveBy(-Layer()->LayoutBoxLocation());
-  HitTestResult layer_result(result.GetHitTestRequest(),
-                             point_in_foreign_object);
-  bool retval = Layer()->HitTest(layer_result);
+  HitTestLocation location(point_in_foreign_object);
+  HitTestResult layer_result(result.GetHitTestRequest(), location);
+  bool retval = Layer()->HitTest(location, layer_result);
 
   // Preserve the "point in inner node frame" from the original request,
   // since |layer_result| is a hit test rooted at the <foreignObject> element,
diff --git a/third_party/blink/renderer/core/layout/svg/layout_svg_foreign_object_test.cc b/third_party/blink/renderer/core/layout/svg/layout_svg_foreign_object_test.cc
index 6736f00c..04b1417 100644
--- a/third_party/blink/renderer/core/layout/svg/layout_svg_foreign_object_test.cc
+++ b/third_party/blink/renderer/core/layout/svg/layout_svg_foreign_object_test.cc
@@ -284,8 +284,9 @@
   EXPECT_EQ(foreignObject, GetDocument().ElementFromPoint(205, 255));
 
   HitTestRequest request(HitTestRequest::kReadOnly | HitTestRequest::kActive);
-  HitTestResult result(request, LayoutPoint(206, 206));
-  GetDocument().GetLayoutView()->Layer()->HitTest(result);
+  HitTestLocation location((LayoutPoint(206, 206)));
+  HitTestResult result(request, location);
+  GetDocument().GetLayoutView()->Layer()->HitTest(location, result);
   EXPECT_EQ(target, result.InnerNode());
   EXPECT_EQ(LayoutPoint(206, 206), result.PointInInnerNodeFrame());
 }
@@ -317,8 +318,9 @@
   EXPECT_EQ(foreignObject, GetDocument().ElementFromPoint(235, 255));
 
   HitTestRequest request(HitTestRequest::kReadOnly | HitTestRequest::kActive);
-  HitTestResult result(request, LayoutPoint(236, 206));
-  GetDocument().GetLayoutView()->Layer()->HitTest(result);
+  HitTestLocation location((LayoutPoint(236, 206)));
+  HitTestResult result(request, location);
+  GetDocument().GetLayoutView()->Layer()->HitTest(location, result);
   EXPECT_EQ(target, result.InnerNode());
   EXPECT_EQ(LayoutPoint(236, 206), result.PointInInnerNodeFrame());
 }
diff --git a/third_party/blink/renderer/core/layout/svg/layout_svg_image.cc b/third_party/blink/renderer/core/layout/svg/layout_svg_image.cc
index cb24c3c6..97934da 100644
--- a/third_party/blink/renderer/core/layout/svg/layout_svg_image.cc
+++ b/third_party/blink/renderer/core/layout/svg/layout_svg_image.cc
@@ -169,9 +169,10 @@
   if (hit_rules.can_hit_fill || hit_rules.can_hit_bounding_box) {
     if (object_bounding_box_.Contains(local_point)) {
       const LayoutPoint& local_layout_point = LayoutPoint(local_point);
+      HitTestLocation location(local_layout_point);
       UpdateHitTestResult(result, local_layout_point);
-      if (result.AddNodeToListBasedTestResult(
-              GetElement(), local_layout_point) == kStopHitTesting)
+      if (result.AddNodeToListBasedTestResult(GetElement(), location) ==
+          kStopHitTesting)
         return true;
     }
   }
diff --git a/third_party/blink/renderer/core/layout/svg/layout_svg_resource_clipper.cc b/third_party/blink/renderer/core/layout/svg/layout_svg_resource_clipper.cc
index ecdf8ca..08521d86 100644
--- a/third_party/blink/renderer/core/layout/svg/layout_svg_resource_clipper.cc
+++ b/third_party/blink/renderer/core/layout/svg/layout_svg_resource_clipper.cc
@@ -244,8 +244,8 @@
        Traversal<SVGElement>::ChildrenOf(*GetElement())) {
     if (!ContributesToClip(child_element))
       continue;
-    IntPoint hit_point;
-    HitTestResult result(HitTestRequest::kSVGClipContent, hit_point);
+    HitTestLocation location((LayoutPoint()));
+    HitTestResult result(HitTestRequest::kSVGClipContent, location);
     LayoutObject* layout_object = child_element.GetLayoutObject();
 
     DCHECK(!layout_object->IsBoxModelObject() ||
diff --git a/third_party/blink/renderer/core/layout/svg/layout_svg_shape.cc b/third_party/blink/renderer/core/layout/svg/layout_svg_shape.cc
index 0cf06a3f..452e6ad 100644
--- a/third_party/blink/renderer/core/layout/svg/layout_svg_shape.cc
+++ b/third_party/blink/renderer/core/layout/svg/layout_svg_shape.cc
@@ -365,7 +365,8 @@
                                hit_rules)) {
     const LayoutPoint& local_layout_point = LayoutPoint(local_point);
     UpdateHitTestResult(result, local_layout_point);
-    if (result.AddNodeToListBasedTestResult(GetElement(), local_layout_point) ==
+    HitTestLocation location(local_layout_point);
+    if (result.AddNodeToListBasedTestResult(GetElement(), location) ==
         kStopHitTesting)
       return true;
   }
diff --git a/third_party/blink/renderer/core/layout/svg/layout_svg_text.cc b/third_party/blink/renderer/core/layout/svg/layout_svg_text.cc
index cbf904e5..a27aece 100644
--- a/third_party/blink/renderer/core/layout/svg/layout_svg_text.cc
+++ b/third_party/blink/renderer/core/layout/svg/layout_svg_text.cc
@@ -328,8 +328,9 @@
         ObjectBoundingBox().Contains(local_point)) {
       const LayoutPoint& local_layout_point = LayoutPoint(local_point);
       UpdateHitTestResult(result, local_layout_point);
-      if (result.AddNodeToListBasedTestResult(
-              GetElement(), local_layout_point) == kStopHitTesting)
+      HitTestLocation location(local_layout_point);
+      if (result.AddNodeToListBasedTestResult(GetElement(), location) ==
+          kStopHitTesting)
         return true;
     }
   }
diff --git a/third_party/blink/renderer/core/page/chrome_client.cc b/third_party/blink/renderer/core/page/chrome_client.cc
index 9030d957..9849c205 100644
--- a/third_party/blink/renderer/core/page/chrome_client.cc
+++ b/third_party/blink/renderer/core/page/chrome_client.cc
@@ -165,6 +165,7 @@
 }
 
 void ChromeClient::MouseDidMoveOverElement(LocalFrame& frame,
+                                           const HitTestLocation& location,
                                            const HitTestResult& result) {
   if (!result.GetScrollbar() && result.InnerNode() &&
       result.InnerNode()->GetDocument().IsDNSPrefetchEnabled())
@@ -175,10 +176,12 @@
   if (result.GetScrollbar())
     ClearToolTip(frame);
   else
-    SetToolTip(frame, result);
+    SetToolTip(frame, location, result);
 }
 
-void ChromeClient::SetToolTip(LocalFrame& frame, const HitTestResult& result) {
+void ChromeClient::SetToolTip(LocalFrame& frame,
+                              const HitTestLocation& location,
+                              const HitTestResult& result) {
   // First priority is a tooltip for element with "title" attribute.
   TextDirection tool_tip_direction;
   String tool_tip = result.Title(tool_tip_direction);
@@ -200,7 +203,7 @@
     }
   }
 
-  if (last_tool_tip_point_ == result.GetHitTestLocation().Point() &&
+  if (last_tool_tip_point_ == location.Point() &&
       last_tool_tip_text_ == tool_tip)
     return;
 
@@ -214,7 +217,7 @@
       !last_tool_tip_text_.IsEmpty() && tool_tip == last_tool_tip_text_)
     ClearToolTip(frame);
 
-  last_tool_tip_point_ = result.GetHitTestLocation().Point();
+  last_tool_tip_point_ = location.Point();
   last_tool_tip_text_ = tool_tip;
   last_mouse_over_node_ = result.InnerNodeOrImageMapImage();
   SetToolTip(frame, tool_tip, tool_tip_direction);
diff --git a/third_party/blink/renderer/core/page/chrome_client.h b/third_party/blink/renderer/core/page/chrome_client.h
index e97b378..721bd508 100644
--- a/third_party/blink/renderer/core/page/chrome_client.h
+++ b/third_party/blink/renderer/core/page/chrome_client.h
@@ -71,6 +71,7 @@
 class HTMLFormControlElement;
 class HTMLInputElement;
 class HTMLSelectElement;
+class HitTestLocation;
 class HitTestResult;
 class IntRect;
 class KeyboardEvent;
@@ -219,7 +220,9 @@
   virtual void ResizeAfterLayout() const {}
   virtual void LayoutUpdated() const {}
 
-  void MouseDidMoveOverElement(LocalFrame&, const HitTestResult&);
+  void MouseDidMoveOverElement(LocalFrame&,
+                               const HitTestLocation&,
+                               const HitTestResult&);
   virtual void SetToolTip(LocalFrame&, const String&, TextDirection) = 0;
   void ClearToolTip(LocalFrame&);
 
@@ -381,7 +384,7 @@
   bool CanOpenModalIfDuringPageDismissal(Frame& main_frame,
                                          DialogType,
                                          const String& message);
-  void SetToolTip(LocalFrame&, const HitTestResult&);
+  void SetToolTip(LocalFrame&, const HitTestLocation&, const HitTestResult&);
 
   WeakMember<Node> last_mouse_over_node_;
   LayoutPoint last_tool_tip_point_;
diff --git a/third_party/blink/renderer/core/page/chrome_client_test.cc b/third_party/blink/renderer/core/page/chrome_client_test.cc
index f30db3b..d97a2f2 100644
--- a/third_party/blink/renderer/core/page/chrome_client_test.cc
+++ b/third_party/blink/renderer/core/page/chrome_client_test.cc
@@ -39,32 +39,32 @@
 TEST_F(ChromeClientTest, SetToolTipFlood) {
   ChromeClientToolTipLogger logger;
   ChromeClient* client = &logger;
-  HitTestResult result(HitTestRequest(HitTestRequest::kMove),
-                       LayoutPoint(10, 20));
+  HitTestLocation location(LayoutPoint(10, 20));
+  HitTestResult result(HitTestRequest(HitTestRequest::kMove), location);
   Document* doc = Document::CreateForTest();
   Element* element = HTMLElement::Create(HTMLNames::divTag, *doc);
   element->setAttribute(HTMLNames::titleAttr, "tooltip");
   result.SetInnerNode(element);
 
-  client->SetToolTip(*doc->GetFrame(), result);
+  client->SetToolTip(*doc->GetFrame(), location, result);
   EXPECT_EQ("tooltip", logger.ToolTipForLastSetToolTip());
 
   // seToolTip(HitTestResult) again in the same condition.
   logger.ClearToolTipForLastSetToolTip();
-  client->SetToolTip(*doc->GetFrame(), result);
+  client->SetToolTip(*doc->GetFrame(), location, result);
   // setToolTip(String,TextDirection) should not be called.
   EXPECT_EQ(String(), logger.ToolTipForLastSetToolTip());
 
   // Cancel the tooltip, and setToolTip(HitTestResult) again.
   client->ClearToolTip(*doc->GetFrame());
   logger.ClearToolTipForLastSetToolTip();
-  client->SetToolTip(*doc->GetFrame(), result);
+  client->SetToolTip(*doc->GetFrame(), location, result);
   // setToolTip(String,TextDirection) should not be called.
   EXPECT_EQ(String(), logger.ToolTipForLastSetToolTip());
 
   logger.ClearToolTipForLastSetToolTip();
   element->setAttribute(HTMLNames::titleAttr, "updated");
-  client->SetToolTip(*doc->GetFrame(), result);
+  client->SetToolTip(*doc->GetFrame(), location, result);
   // setToolTip(String,TextDirection) should be called because tooltip string
   // is different from the last one.
   EXPECT_EQ("updated", logger.ToolTipForLastSetToolTip());
diff --git a/third_party/blink/renderer/core/page/context_menu_controller.cc b/third_party/blink/renderer/core/page/context_menu_controller.cc
index 1d1a0b7..abfaa89d 100644
--- a/third_party/blink/renderer/core/page/context_menu_controller.cc
+++ b/third_party/blink/renderer/core/page/context_menu_controller.cc
@@ -179,7 +179,7 @@
 }
 
 bool ContextMenuController::ShowContextMenu(LocalFrame* frame,
-                                            const LayoutPoint& location,
+                                            const LayoutPoint& point,
                                             WebMenuSourceType source_type) {
   // Displaying the context menu in this function is a big hack as we don't
   // have context, i.e. whether this is being invoked via a script or in
@@ -191,20 +191,21 @@
 
   HitTestRequest::HitTestRequestType type =
       HitTestRequest::kReadOnly | HitTestRequest::kActive;
-  HitTestResult r(type, location);
+  HitTestLocation location(point);
+  HitTestResult result(type, location);
   if (frame)
-    r = frame->GetEventHandler().HitTestResultAtPoint(location, type);
-  if (!r.InnerNodeOrImageMapImage())
+    result = frame->GetEventHandler().HitTestResultAtLocation(location, type);
+  if (!result.InnerNodeOrImageMapImage())
     return false;
 
-  hit_test_result_ = r;
-  r.SetToShadowHostIfInRestrictedShadowRoot();
+  hit_test_result_ = result;
+  result.SetToShadowHostIfInRestrictedShadowRoot();
 
-  LocalFrame* selected_frame = r.InnerNodeFrame();
+  LocalFrame* selected_frame = result.InnerNodeFrame();
 
   WebContextMenuData data;
-  data.mouse_position =
-      selected_frame->View()->FrameToViewport(r.RoundedPointInInnerNodeFrame());
+  data.mouse_position = selected_frame->View()->FrameToViewport(
+      result.RoundedPointInInnerNodeFrame());
 
   data.edit_flags = ComputeEditFlags(
       *selected_frame->GetDocument(),
@@ -213,10 +214,10 @@
 
   // Links, Images, Media tags, and Image/Media-Links take preference over
   // all else.
-  data.link_url = r.AbsoluteLinkURL();
+  data.link_url = result.AbsoluteLinkURL();
 
-  if (r.InnerNode()->IsHTMLElement()) {
-    HTMLElement* html_element = ToHTMLElement(r.InnerNode());
+  if (result.InnerNode()->IsHTMLElement()) {
+    HTMLElement* html_element = ToHTMLElement(result.InnerNode());
     if (!html_element->title().IsEmpty()) {
       data.title_text = html_element->title();
     } else {
@@ -224,34 +225,34 @@
     }
   }
 
-  if (IsHTMLCanvasElement(r.InnerNode())) {
+  if (IsHTMLCanvasElement(result.InnerNode())) {
     data.media_type = WebContextMenuData::kMediaTypeCanvas;
     data.has_image_contents = true;
-  } else if (!r.AbsoluteImageURL().IsEmpty()) {
-    data.src_url = r.AbsoluteImageURL();
+  } else if (!result.AbsoluteImageURL().IsEmpty()) {
+    data.src_url = result.AbsoluteImageURL();
     data.media_type = WebContextMenuData::kMediaTypeImage;
     data.media_flags |= WebContextMenuData::kMediaCanPrint;
 
     // An image can be null for many reasons, like being blocked, no image
     // data received from server yet.
-    data.has_image_contents = r.GetImage() && !r.GetImage()->IsNull();
+    data.has_image_contents = result.GetImage() && !result.GetImage()->IsNull();
     data.is_placeholder_image =
-        r.GetImage() && r.GetImage()->IsPlaceholderImage();
+        result.GetImage() && result.GetImage()->IsPlaceholderImage();
     if (data.has_image_contents &&
-        IsHTMLImageElement(r.InnerNodeOrImageMapImage())) {
+        IsHTMLImageElement(result.InnerNodeOrImageMapImage())) {
       HTMLImageElement* image_element =
-          ToHTMLImageElement(r.InnerNodeOrImageMapImage());
+          ToHTMLImageElement(result.InnerNodeOrImageMapImage());
       if (image_element && image_element->CachedImage()) {
         data.image_response = WrappedResourceResponse(
             image_element->CachedImage()->GetResponse());
       }
     }
-  } else if (!r.AbsoluteMediaURL().IsEmpty()) {
-    data.src_url = r.AbsoluteMediaURL();
+  } else if (!result.AbsoluteMediaURL().IsEmpty()) {
+    data.src_url = result.AbsoluteMediaURL();
 
     // We know that if absoluteMediaURL() is not empty, then this
     // is a media element.
-    HTMLMediaElement* media_element = ToHTMLMediaElement(r.InnerNode());
+    HTMLMediaElement* media_element = ToHTMLMediaElement(result.InnerNode());
     if (IsHTMLVideoElement(*media_element)) {
       // A video element should be presented as an audio element when it has an
       // audio track but no video track.
@@ -291,9 +292,9 @@
       data.media_flags |= WebContextMenuData::kMediaCanToggleControls;
     if (media_element->ShouldShowControls())
       data.media_flags |= WebContextMenuData::kMediaControls;
-  } else if (IsHTMLObjectElement(*r.InnerNode()) ||
-             IsHTMLEmbedElement(*r.InnerNode())) {
-    LayoutObject* object = r.InnerNode()->GetLayoutObject();
+  } else if (IsHTMLObjectElement(*result.InnerNode()) ||
+             IsHTMLEmbedElement(*result.InnerNode())) {
+    LayoutObject* object = result.InnerNode()->GetLayoutObject();
     if (object && object->IsLayoutEmbeddedContent()) {
       WebPluginContainerImpl* plugin_view =
           ToLayoutEmbeddedContent(object)->Plugin();
@@ -303,7 +304,8 @@
         WebPlugin* plugin = plugin_view->Plugin();
         data.link_url = plugin->LinkAtPosition(data.mouse_position);
 
-        HTMLPlugInElement* plugin_element = ToHTMLPlugInElement(r.InnerNode());
+        HTMLPlugInElement* plugin_element =
+            ToHTMLPlugInElement(result.InnerNode());
         data.src_url =
             plugin_element->GetDocument().CompleteURL(plugin_element->Url());
 
@@ -373,7 +375,8 @@
   // If source_type is |kMenuSourceAdjustSelection| or
   // |kMenuSourceAdjustSelectionReset| we know the original HitTestResult in
   // SelectionController passed the inside check already, so let it pass.
-  if (r.IsSelected() || source_type == kMenuSourceAdjustSelection ||
+  if (result.IsSelected(location) ||
+      source_type == kMenuSourceAdjustSelection ||
       source_type == kMenuSourceAdjustSelectionReset) {
     data.selected_text = selected_frame->SelectedText();
     WebRange range =
@@ -381,7 +384,7 @@
     data.selection_start_offset = range.StartOffset();
   }
 
-  if (r.IsContentEditable()) {
+  if (result.IsContentEditable()) {
     data.is_editable = true;
     SpellChecker& spell_checker = selected_frame->GetSpellChecker();
 
@@ -424,7 +427,7 @@
     data.custom_items = menu_provider_->PopulateContextMenu();
   }
 
-  if (auto* anchor = ToHTMLAnchorElementOrNull(r.URLElement())) {
+  if (auto* anchor = ToHTMLAnchorElementOrNull(result.URLElement())) {
     // Extract suggested filename for same-origin URLS for saving file.
     const SecurityOrigin* origin =
         selected_frame->GetSecurityContext()->GetSecurityOrigin();
@@ -442,7 +445,7 @@
   }
 
   // Find the input field type.
-  if (auto* input = ToHTMLInputElementOrNull(r.InnerNode())) {
+  if (auto* input = ToHTMLInputElementOrNull(result.InnerNode())) {
     if (input->type() == InputTypeNames::password)
       data.input_field_type = WebContextMenuData::kInputFieldTypePassword;
     else if (input->IsTextField())
diff --git a/third_party/blink/renderer/core/page/drag_controller.cc b/third_party/blink/renderer/core/page/drag_controller.cc
index 32e6f5c..888bad9f 100644
--- a/third_party/blink/renderer/core/page/drag_controller.cc
+++ b/third_party/blink/renderer/core/page/drag_controller.cc
@@ -261,9 +261,10 @@
       if (!prevented_default) {
         // When drop target is plugin element and it can process drag, we
         // should prevent default behavior.
-        const LayoutPoint point = local_root.View()->ConvertFromRootFrame(
-            LayoutPoint(drag_data->ClientPosition()));
-        const HitTestResult result = event_handler.HitTestResultAtPoint(point);
+        const HitTestLocation location(local_root.View()->ConvertFromRootFrame(
+            LayoutPoint(drag_data->ClientPosition())));
+        const HitTestResult result =
+            event_handler.HitTestResultAtLocation(location);
         prevented_default |=
             IsHTMLPlugInElement(*result.InnerNode()) &&
             ToHTMLPlugInElement(result.InnerNode())->CanProcessDrag();
@@ -355,8 +356,9 @@
 static Element* ElementUnderMouse(Document* document_under_mouse,
                                   const LayoutPoint& point) {
   HitTestRequest request(HitTestRequest::kReadOnly | HitTestRequest::kActive);
-  HitTestResult result(request, point);
-  document_under_mouse->GetLayoutView()->HitTest(result);
+  HitTestLocation location(point);
+  HitTestResult result(request, location);
+  document_under_mouse->GetLayoutView()->HitTest(location, result);
 
   Node* n = result.InnerNode();
   while (n && !n->IsElementNode())
@@ -698,8 +700,9 @@
   LayoutPoint point = local_root.View()->ConvertFromRootFrame(
       LayoutPoint(drag_data->ClientPosition()));
 
+  HitTestLocation location(point);
   HitTestResult result =
-      local_root.GetEventHandler().HitTestResultAtPoint(point);
+      local_root.GetEventHandler().HitTestResultAtLocation(location);
 
   if (!result.InnerNode())
     return false;
@@ -716,7 +719,7 @@
   }
 
   if (did_initiate_drag_ && document_under_mouse_ == drag_initiator_ &&
-      result.IsSelected())
+      result.IsSelected(location))
     return false;
 
   return true;
@@ -921,8 +924,9 @@
   if (!src->View() || !src->ContentLayoutObject())
     return false;
 
+  HitTestLocation location(drag_origin);
   HitTestResult hit_test_result =
-      src->GetEventHandler().HitTestResultAtPoint(drag_origin);
+      src->GetEventHandler().HitTestResultAtLocation(location);
   // FIXME: Can this even happen? I guess it's possible, but should verify
   // with a layout test.
   if (!state.drag_src_->IsShadowIncludingInclusiveAncestorOf(
@@ -1156,8 +1160,9 @@
   if (!src->View() || !src->ContentLayoutObject())
     return false;
 
+  HitTestLocation location(drag_origin);
   HitTestResult hit_test_result =
-      src->GetEventHandler().HitTestResultAtPoint(drag_origin);
+      src->GetEventHandler().HitTestResultAtLocation(location);
   if (!state.drag_src_->IsShadowIncludingInclusiveAncestorOf(
           hit_test_result.InnerNode())) {
     // The original node being dragged isn't under the drag origin anymore...
diff --git a/third_party/blink/renderer/core/page/event_with_hit_test_results.h b/third_party/blink/renderer/core/page/event_with_hit_test_results.h
index 153bc8a..e31396a 100644
--- a/third_party/blink/renderer/core/page/event_with_hit_test_results.h
+++ b/third_party/blink/renderer/core/page/event_with_hit_test_results.h
@@ -35,8 +35,11 @@
 
  public:
   EventWithHitTestResults(const EventType& event,
+                          const HitTestLocation& location,
                           const HitTestResult& hit_test_result)
-      : event_(event), hit_test_result_(hit_test_result) {}
+      : event_(event),
+        hit_test_result_(hit_test_result),
+        hit_test_location_(location) {}
 
   const EventType& Event() const { return event_; }
   const HitTestResult& GetHitTestResult() const { return hit_test_result_; }
@@ -52,9 +55,17 @@
     return hit_test_result_.CanvasRegionId();
   }
 
+  const HitTestLocation& GetHitTestLocation() const {
+    return hit_test_location_;
+  }
+  void SetHitTestLocation(const HitTestLocation& new_location) {
+    hit_test_location_ = new_location;
+  }
+
  private:
   EventType event_;
   HitTestResult hit_test_result_;
+  HitTestLocation hit_test_location_;
 };
 
 using MouseEventWithHitTestResults = EventWithHitTestResults<WebMouseEvent>;
diff --git a/third_party/blink/renderer/core/page/focus_controller.cc b/third_party/blink/renderer/core/page/focus_controller.cc
index da3d74c..6024acc3 100644
--- a/third_party/blink/renderer/core/page/focus_controller.cc
+++ b/third_party/blink/renderer/core/page/focus_controller.cc
@@ -1292,15 +1292,15 @@
              ->MainFrame()
              ->IsLocalFrame())
       return;
+    HitTestLocation location(IntPoint(x.ToInt(), y.ToInt()));
     HitTestResult result =
         candidate.visible_node->GetDocument()
             .GetPage()
             ->DeprecatedLocalMainFrame()
             ->GetEventHandler()
-            .HitTestResultAtPoint(IntPoint(x.ToInt(), y.ToInt()),
-                                  HitTestRequest::kReadOnly |
-                                      HitTestRequest::kActive |
-                                      HitTestRequest::kIgnoreClipping);
+            .HitTestResultAtLocation(
+                location, HitTestRequest::kReadOnly | HitTestRequest::kActive |
+                              HitTestRequest::kIgnoreClipping);
     if (candidate.visible_node->contains(result.InnerNode())) {
       closest = candidate;
       return;
diff --git a/third_party/blink/renderer/core/page/page_animator.cc b/third_party/blink/renderer/core/page/page_animator.cc
index 3096104..7b3fedded 100644
--- a/third_party/blink/renderer/core/page/page_animator.cc
+++ b/third_party/blink/renderer/core/page/page_animator.cc
@@ -72,7 +72,6 @@
               monotonic_animation_start_time.since_origin().InSecondsF());
         }
       }
-      document->GetFrame()->AnimateSnapFling(monotonic_animation_start_time);
       SVGDocumentExtensions::ServiceOnAnimationFrame(*document);
     }
     // TODO(skyostil): This function should not run for documents without views.
diff --git a/third_party/blink/renderer/core/page/page_widget_delegate.cc b/third_party/blink/renderer/core/page/page_widget_delegate.cc
index 2d0aaf56..59ed089 100644
--- a/third_party/blink/renderer/core/page/page_widget_delegate.cc
+++ b/third_party/blink/renderer/core/page/page_widget_delegate.cc
@@ -140,10 +140,10 @@
     WebMouseEvent mouse_event = TransformWebMouseEvent(
         root->View(), static_cast<const WebMouseEvent&>(event));
 
-    IntPoint doc_point(root->View()->ConvertFromRootFrame(
+    HitTestLocation location(root->View()->ConvertFromRootFrame(
         FlooredIntPoint(mouse_event.PositionInRootFrame())));
-    HitTestResult result = root->GetEventHandler().HitTestResultAtPoint(
-        doc_point, HitTestRequest::kReadOnly | HitTestRequest::kActive);
+    HitTestResult result = root->GetEventHandler().HitTestResultAtLocation(
+        location, HitTestRequest::kReadOnly | HitTestRequest::kActive);
     result.SetToShadowHostIfInRestrictedShadowRoot();
     if (result.InnerNodeFrame()) {
       Document* document = result.InnerNodeFrame()->GetDocument();
diff --git a/third_party/blink/renderer/core/page/scrolling/snap_coordinator.cc b/third_party/blink/renderer/core/page/scrolling/snap_coordinator.cc
index 83ca617..33fcee6 100644
--- a/third_party/blink/renderer/core/page/scrolling/snap_coordinator.cc
+++ b/third_party/blink/renderer/core/page/scrolling/snap_coordinator.cc
@@ -235,26 +235,25 @@
   return snap_area_data;
 }
 
-base::Optional<FloatPoint> SnapCoordinator::GetSnapPositionForPoint(
+FloatPoint SnapCoordinator::GetSnapPositionForPoint(
     const LayoutBox& snap_container,
     const FloatPoint& point,
     bool did_scroll_x,
     bool did_scroll_y) {
   auto iter = snap_container_map_.find(&snap_container);
   if (iter == snap_container_map_.end())
-    return base::nullopt;
+    return point;
 
   const SnapContainerData& data = iter->value;
   if (!data.size())
-    return base::nullopt;
+    return point;
 
   gfx::ScrollOffset snap_position;
   if (data.FindSnapPosition(gfx::ScrollOffset(point.X(), point.Y()),
                             did_scroll_x, did_scroll_y, &snap_position)) {
-    FloatPoint snap_point(snap_position.x(), snap_position.y());
-    return snap_point;
+    return FloatPoint(snap_position.x(), snap_position.y());
   }
-  return base::nullopt;
+  return point;
 }
 
 void SnapCoordinator::PerformSnapping(const LayoutBox& snap_container,
@@ -265,14 +264,12 @@
     return;
 
   FloatPoint current_position = scrollable_area->ScrollPosition();
-  base::Optional<FloatPoint> snap_point = GetSnapPositionForPoint(
+  FloatPoint snap_position = GetSnapPositionForPoint(
       snap_container, current_position, did_scroll_x, did_scroll_y);
-  if (!snap_point.has_value())
-    return;
 
-  if (snap_point.value() != current_position) {
+  if (snap_position != current_position) {
     scrollable_area->SetScrollOffset(
-        scrollable_area->ScrollPositionToOffset(snap_point.value()),
+        scrollable_area->ScrollPositionToOffset(snap_position),
         kProgrammaticScroll, kScrollBehaviorSmooth);
   }
 }
@@ -305,30 +302,6 @@
   return base::nullopt;
 }
 
-bool SnapCoordinator::GetSnapFlingInfo(
-    const LayoutBox& snap_container,
-    const gfx::Vector2dF& natural_displacement,
-    gfx::Vector2dF* out_initial_offset,
-    gfx::Vector2dF* out_target_offset) {
-  ScrollableArea* scrollable_area = ScrollableAreaForSnapping(snap_container);
-  if (!scrollable_area)
-    return false;
-
-  FloatPoint current_position = scrollable_area->ScrollPosition();
-  *out_initial_offset = gfx::Vector2dF(current_position);
-  FloatPoint original_end =
-      current_position +
-      FloatPoint(natural_displacement.x(), natural_displacement.y());
-  bool did_scroll_x = natural_displacement.x() != 0;
-  bool did_scroll_y = natural_displacement.y() != 0;
-  base::Optional<FloatPoint> snap_end = GetSnapPositionForPoint(
-      snap_container, original_end, did_scroll_x, did_scroll_y);
-  if (!snap_end.has_value())
-    return false;
-  *out_target_offset = gfx::Vector2dF(snap_end.value());
-  return true;
-}
-
 #ifndef NDEBUG
 
 void SnapCoordinator::ShowSnapAreaMap() {
diff --git a/third_party/blink/renderer/core/page/scrolling/snap_coordinator.h b/third_party/blink/renderer/core/page/scrolling/snap_coordinator.h
index 2c3ad76..d21599a 100644
--- a/third_party/blink/renderer/core/page/scrolling/snap_coordinator.h
+++ b/third_party/blink/renderer/core/page/scrolling/snap_coordinator.h
@@ -57,15 +57,10 @@
   void PerformSnapping(const LayoutBox& snap_container,
                        bool did_scroll_x,
                        bool did_scroll_y);
-  base::Optional<FloatPoint> GetSnapPositionForPoint(
-      const LayoutBox& snap_container,
-      const FloatPoint& natural_position,
-      bool did_scroll_x,
-      bool did_scroll_y);
-  bool GetSnapFlingInfo(const LayoutBox& snap_container,
-                        const gfx::Vector2dF& natural_displacement,
-                        gfx::Vector2dF* out_initial_offset,
-                        gfx::Vector2dF* out_target_offset);
+  FloatPoint GetSnapPositionForPoint(const LayoutBox& snap_container,
+                                     const FloatPoint& natural_position,
+                                     bool did_scroll_x,
+                                     bool did_scroll_y);
 
 #ifndef NDEBUG
   void ShowSnapAreaMap();
diff --git a/third_party/blink/renderer/core/page/scrolling/snap_coordinator_test.cc b/third_party/blink/renderer/core/page/scrolling/snap_coordinator_test.cc
index 119f7ba3..4fc0260 100644
--- a/third_party/blink/renderer/core/page/scrolling/snap_coordinator_test.cc
+++ b/third_party/blink/renderer/core/page/scrolling/snap_coordinator_test.cc
@@ -642,12 +642,10 @@
 
   SnapCoordinator* snap_coordinator = GetDocument().GetSnapCoordinator();
   LayoutBox* snap_container = scroller_element->GetLayoutBox();
-  base::Optional<FloatPoint> snap_position =
-      snap_coordinator->GetSnapPositionForPoint(
-          *snap_container, FloatPoint(150, 150), true, false);
-  EXPECT_TRUE(snap_position.has_value());
-  EXPECT_EQ(200 - 8 - 10, snap_position.value().X());
-  EXPECT_EQ(150, snap_position.value().Y());
+  FloatPoint snap_position = snap_coordinator->GetSnapPositionForPoint(
+      *snap_container, FloatPoint(150, 150), true, false);
+  EXPECT_EQ(200 - 8 - 10, snap_position.X());
+  EXPECT_EQ(150, snap_position.Y());
 }
 
 TEST_F(SnapCoordinatorTest, DoesNotSnapOnNonSnappingAxis) {
@@ -660,10 +658,10 @@
 
   SnapCoordinator* snap_coordinator = GetDocument().GetSnapCoordinator();
   LayoutBox* snap_container = scroller_element->GetLayoutBox();
-  base::Optional<FloatPoint> snap_position =
-      snap_coordinator->GetSnapPositionForPoint(
-          *snap_container, FloatPoint(150, 150), true, false);
-  EXPECT_FALSE(snap_position.has_value());
+  FloatPoint snap_position = snap_coordinator->GetSnapPositionForPoint(
+      *snap_container, FloatPoint(150, 150), true, false);
+  EXPECT_EQ(150, snap_position.X());
+  EXPECT_EQ(150, snap_position.Y());
 }
 
 TEST_F(SnapCoordinatorTest, DoesNotSnapOnEmptyContainer) {
@@ -676,11 +674,10 @@
 
   SnapCoordinator* snap_coordinator = GetDocument().GetSnapCoordinator();
   LayoutBox* snap_container = scroller_element->GetLayoutBox();
-  base::Optional<FloatPoint> snap_position =
-      snap_coordinator->GetSnapPositionForPoint(
-          *snap_container, FloatPoint(150, 150), true, false);
-  ;
-  EXPECT_FALSE(snap_position.has_value());
+  FloatPoint snap_position = snap_coordinator->GetSnapPositionForPoint(
+      *snap_container, FloatPoint(150, 150), true, false);
+  EXPECT_EQ(150, snap_position.X());
+  EXPECT_EQ(150, snap_position.Y());
 }
 
 TEST_F(SnapCoordinatorTest, DoesNotSnapOnNonSnapContainer) {
@@ -693,10 +690,10 @@
 
   SnapCoordinator* snap_coordinator = GetDocument().GetSnapCoordinator();
   LayoutBox* snap_container = scroller_element->GetLayoutBox();
-  base::Optional<FloatPoint> snap_position =
-      snap_coordinator->GetSnapPositionForPoint(
-          *snap_container, FloatPoint(150, 150), true, false);
-  EXPECT_FALSE(snap_position.has_value());
+  FloatPoint snap_position = snap_coordinator->GetSnapPositionForPoint(
+      *snap_container, FloatPoint(150, 150), true, false);
+  EXPECT_EQ(150, snap_position.X());
+  EXPECT_EQ(150, snap_position.Y());
 }
 
 }  // namespace
diff --git a/third_party/blink/renderer/core/page/touch_adjustment.cc b/third_party/blink/renderer/core/page/touch_adjustment.cc
index e096036..7f43491 100644
--- a/third_party/blink/renderer/core/page/touch_adjustment.cc
+++ b/third_party/blink/renderer/core/page/touch_adjustment.cc
@@ -389,7 +389,7 @@
 
 // Adjusts 'point' to the nearest point inside rect, and leaves it unchanged if
 // already inside.
-void AdjustPointToRect(FloatPoint& point, const FloatRect& rect) {
+void AdjustPointToRect(FloatPoint& point, const IntRect& rect) {
   if (point.X() < rect.X())
     point.SetX(rect.X());
   else if (point.X() > rect.MaxX())
diff --git a/third_party/blink/renderer/core/page/touch_disambiguation.cc b/third_party/blink/renderer/core/page/touch_disambiguation.cc
index a888dced..f6678b7 100644
--- a/third_party/blink/renderer/core/page/touch_disambiguation.cc
+++ b/third_party/blink/renderer/core/page/touch_disambiguation.cc
@@ -89,9 +89,10 @@
   good_targets.clear();
   LayoutPoint hit_point(main_frame->View()->ConvertFromRootFrame(
       touch_box_in_root_frame.Location()));
-  LayoutRect hit_rect(hit_point, LayoutSize(touch_box_in_root_frame.Size()));
-  HitTestResult result = main_frame->GetEventHandler().HitTestResultAtRect(
-      hit_rect, HitTestRequest::kReadOnly | HitTestRequest::kActive |
+  HitTestLocation location(
+      LayoutRect(hit_point, LayoutSize(touch_box_in_root_frame.Size())));
+  HitTestResult result = main_frame->GetEventHandler().HitTestResultAtLocation(
+      location, HitTestRequest::kReadOnly | HitTestRequest::kActive |
                     HitTestRequest::kListBased);
   const HeapListHashSet<Member<Node>>& hit_results =
       result.ListBasedTestResult();
diff --git a/third_party/blink/renderer/core/paint/paint_and_raster_invalidation_test.cc b/third_party/blink/renderer/core/paint/paint_and_raster_invalidation_test.cc
index cd3944e..b4e59a1 100644
--- a/third_party/blink/renderer/core/paint/paint_and_raster_invalidation_test.cc
+++ b/third_party/blink/renderer/core/paint/paint_and_raster_invalidation_test.cc
@@ -825,6 +825,35 @@
   EXPECT_EQ(LayoutRect(300, 0, 150, 20), c->FirstFragment().VisualRect());
 };
 
+TEST_P(PaintAndRasterInvalidationTest, PaintPropertyChange) {
+  SetUpHTML(*this);
+  Element* target = GetDocument().getElementById("target");
+  auto* object = target->GetLayoutObject();
+  target->setAttribute(HTMLNames::classAttr, "background transform");
+  GetDocument().View()->UpdateAllLifecyclePhases();
+
+  auto* layer = ToLayoutBoxModelObject(object)->Layer();
+  GetDocument().View()->SetTracksPaintInvalidations(true);
+  target->setAttribute(HTMLNames::styleAttr, "transform: scale(3)");
+  GetDocument().View()->UpdateAllLifecyclePhasesExceptPaint();
+  EXPECT_FALSE(layer->NeedsRepaint());
+  const auto* transform =
+      object->FirstFragment().PaintProperties()->Transform();
+  EXPECT_TRUE(transform->Changed(*transform->Parent()));
+
+  GetDocument().View()->UpdateAllLifecyclePhases();
+  EXPECT_THAT(GetRasterInvalidationTracking()->Invalidations(),
+              UnorderedElementsAre(
+                  RasterInvalidationInfo{
+                      layer, layer->DebugName(), IntRect(0, 0, 100, 200),
+                      PaintInvalidationReason::kPaintProperty},
+                  RasterInvalidationInfo{
+                      layer, layer->DebugName(), IntRect(0, 0, 150, 300),
+                      PaintInvalidationReason::kPaintProperty}));
+  EXPECT_FALSE(transform->Changed(*transform->Parent()));
+  GetDocument().View()->SetTracksPaintInvalidations(false);
+}
+
 class PaintInvalidatorTestClient : public EmptyChromeClient {
  public:
   void InvalidateRect(const IntRect&) override {
diff --git a/third_party/blink/renderer/core/paint/paint_layer.cc b/third_party/blink/renderer/core/paint/paint_layer.cc
index 2f65372..e6d56bd1 100644
--- a/third_party/blink/renderer/core/paint/paint_layer.cc
+++ b/third_party/blink/renderer/core/paint/paint_layer.cc
@@ -1791,12 +1791,15 @@
 
 PaintLayer::HitTestRecursionData::HitTestRecursionData(
     const LayoutRect& rect_arg,
-    const HitTestLocation& location_arg)
+    const HitTestLocation& location_arg,
+    const HitTestLocation& original_location_arg)
     : rect(rect_arg),
       location(location_arg),
+      original_location(original_location_arg),
       intersects_location(location_arg.Intersects(rect_arg)) {}
 
-bool PaintLayer::HitTest(HitTestResult& result) {
+bool PaintLayer::HitTest(const HitTestLocation& hit_test_location,
+                         HitTestResult& result) {
   DCHECK(IsSelfPaintingLayer() || HasSelfPaintingLayerDescendant());
 
   // LayoutView should make sure to update layout before entering hit testing
@@ -1804,7 +1807,6 @@
   DCHECK(!GetLayoutObject().GetDocument().GetLayoutView()->NeedsLayout());
 
   const HitTestRequest& request = result.GetHitTestRequest();
-  const HitTestLocation& hit_test_location = result.GetHitTestLocation();
 
   // Start with frameVisibleRect to ensure we include the scrollbars.
   LayoutRect hit_test_area = FrameVisibleRect(GetLayoutObject());
@@ -1815,7 +1817,8 @@
     }
   }
 
-  HitTestRecursionData recursion_data(hit_test_area, hit_test_location);
+  HitTestRecursionData recursion_data(hit_test_area, hit_test_location,
+                                      hit_test_location);
   PaintLayer* inside_layer =
       HitTestLayer(this, nullptr, result, recursion_data, false);
   if (!inside_layer && IsRootLayer()) {
@@ -2147,7 +2150,7 @@
       // Hit test with a temporary HitTestResult, because we only want to commit
       // to 'result' if we know we're frontmost.
       HitTestResult temp_result(result.GetHitTestRequest(),
-                                result.GetHitTestLocation());
+                                recursion_data.original_location);
       bool inside_fragment_foreground_rect = false;
 
       if (HitTestContentsForFragments(
@@ -2189,7 +2192,7 @@
 
   if (recursion_data.intersects_location && IsSelfPaintingLayer()) {
     HitTestResult temp_result(result.GetHitTestRequest(),
-                              result.GetHitTestLocation());
+                              recursion_data.original_location);
     bool inside_fragment_background_rect = false;
     if (HitTestContentsForFragments(*layer_fragments, offset, temp_result,
                                     recursion_data.location, kHitTestSelf,
@@ -2303,7 +2306,8 @@
     new_location.emplace(local_point, local_point_quad);
   else
     new_location.emplace(local_point);
-  HitTestRecursionData new_recursion_data(bounds_of_mapped_area, *new_location);
+  HitTestRecursionData new_recursion_data(bounds_of_mapped_area, *new_location,
+                                          recursion_data.original_location);
 
   // Now do a hit test with the root layer shifted to be us.
   return HitTestLayer(this, container_layer, result, new_recursion_data, true,
@@ -2416,7 +2420,7 @@
 
     PaintLayer* hit_layer = nullptr;
     HitTestResult temp_result(result.GetHitTestRequest(),
-                              result.GetHitTestLocation());
+                              recursion_data.original_location);
     hit_layer = child_layer->HitTestLayer(
         root_layer, this, temp_result, recursion_data, false, transform_state,
         z_offset_for_descendants);
diff --git a/third_party/blink/renderer/core/paint/paint_layer.h b/third_party/blink/renderer/core/paint/paint_layer.h
index e53b90aa..6df1f61 100644
--- a/third_party/blink/renderer/core/paint/paint_layer.h
+++ b/third_party/blink/renderer/core/paint/paint_layer.h
@@ -422,7 +422,7 @@
 
   // The hitTest() method looks for mouse events by walking layers that
   // intersect the point from front to back.
-  bool HitTest(HitTestResult&);
+  bool HitTest(const HitTestLocation& location, HitTestResult&);
 
   bool IntersectsDamageRect(const LayoutRect& layer_bounds,
                             const LayoutRect& damage_rect,
@@ -1116,9 +1116,11 @@
     const LayoutRect& rect;
     // Whether location.Intersects(rect) returns true.
     const HitTestLocation& location;
+    const HitTestLocation& original_location;
     const bool intersects_location;
     HitTestRecursionData(const LayoutRect& rect_arg,
-                         const HitTestLocation& location_arg);
+                         const HitTestLocation& location_arg,
+                         const HitTestLocation& original_location_arg);
   };
 
   PaintLayer* HitTestLayer(PaintLayer* root_layer,
diff --git a/third_party/blink/renderer/core/paint/paint_layer_scrollable_area_test.cc b/third_party/blink/renderer/core/paint/paint_layer_scrollable_area_test.cc
index b3b8cc3c..4d989ee5 100644
--- a/third_party/blink/renderer/core/paint/paint_layer_scrollable_area_test.cc
+++ b/third_party/blink/renderer/core/paint/paint_layer_scrollable_area_test.cc
@@ -911,20 +911,24 @@
   scrollable_area->SetScrollbarsHiddenIfOverlay(true);
 
   HitTestRequest hit_request(HitTestRequest::kMove | HitTestRequest::kReadOnly);
-  HitTestResult hit_result(hit_request, LayoutPoint(95, 5));
-  GetDocument().GetLayoutView()->HitTest(hit_result);
+  HitTestLocation location(LayoutPoint(95, 5));
+  HitTestResult hit_result(hit_request, location);
+  GetDocument().GetLayoutView()->HitTest(location, hit_result);
   EXPECT_EQ(hit_result.GetScrollbar(), nullptr);
-  hit_result = HitTestResult(hit_request, LayoutPoint(5, 95));
-  GetDocument().GetLayoutView()->HitTest(hit_result);
+  location = HitTestLocation(LayoutPoint(5, 95));
+  hit_result = HitTestResult(hit_request, location);
+  GetDocument().GetLayoutView()->HitTest(location, hit_result);
   EXPECT_EQ(hit_result.GetScrollbar(), nullptr);
 
   scrollable_area->SetScrollbarsHiddenIfOverlay(false);
 
-  hit_result = HitTestResult(hit_request, LayoutPoint(95, 5));
-  GetDocument().GetLayoutView()->HitTest(hit_result);
+  location = HitTestLocation(LayoutPoint(95, 5));
+  hit_result = HitTestResult(hit_request, location);
+  GetDocument().GetLayoutView()->HitTest(location, hit_result);
   EXPECT_EQ(hit_result.GetScrollbar(), scrollable_area->VerticalScrollbar());
-  hit_result = HitTestResult(hit_request, LayoutPoint(5, 95));
-  GetDocument().GetLayoutView()->HitTest(hit_result);
+  location = HitTestLocation(LayoutPoint(5, 95));
+  hit_result = HitTestResult(hit_request, location);
+  GetDocument().GetLayoutView()->HitTest(location, hit_result);
   EXPECT_EQ(hit_result.GetScrollbar(), scrollable_area->HorizontalScrollbar());
 }
 
diff --git a/third_party/blink/renderer/core/paint/paint_layer_test.cc b/third_party/blink/renderer/core/paint/paint_layer_test.cc
index c66b7c1..6cc9ba6 100644
--- a/third_party/blink/renderer/core/paint/paint_layer_test.cc
+++ b/third_party/blink/renderer/core/paint/paint_layer_test.cc
@@ -1354,8 +1354,9 @@
 
   HitTestRequest request(HitTestRequest::kIgnoreClipping);
   // (10, 900) is outside the viewport clip of 800x600.
-  HitTestResult result(request, IntPoint(10, 900));
-  GetDocument().GetLayoutView()->HitTest(result);
+  HitTestLocation location((IntPoint(10, 900)));
+  HitTestResult result(request, location);
+  GetDocument().GetLayoutView()->HitTest(location, result);
   EXPECT_EQ(GetDocument().getElementById("hit"), result.InnerNode());
 }
 
@@ -1372,37 +1373,40 @@
 
   // Regular hit test over 'child'
   HitTestRequest request(HitTestRequest::kReadOnly | HitTestRequest::kActive);
-  HitTestResult result(request, LayoutPoint(50, 25));
-  GetDocument().GetLayoutView()->Layer()->HitTest(result);
+  HitTestLocation location((LayoutPoint(50, 25)));
+  HitTestResult result(request, location);
+  GetDocument().GetLayoutView()->Layer()->HitTest(location, result);
   EXPECT_EQ(child, result.InnerNode());
 
   // Same hit test, with stop node.
   request = HitTestRequest(HitTestRequest::kReadOnly | HitTestRequest::kActive,
                            hit->GetLayoutObject());
-  result = HitTestResult(request, LayoutPoint(50, 25));
-  GetDocument().GetLayoutView()->Layer()->HitTest(result);
+  result = HitTestResult(request, location);
+  GetDocument().GetLayoutView()->Layer()->HitTest(location, result);
   EXPECT_EQ(hit, result.InnerNode());
 
   // Regular hit test over 'overlap'
   request = HitTestRequest(HitTestRequest::kReadOnly | HitTestRequest::kActive);
-  result = HitTestResult(request, LayoutPoint(50, 75));
-  GetDocument().GetLayoutView()->Layer()->HitTest(result);
+  location = HitTestLocation((LayoutPoint(50, 75)));
+  result = HitTestResult(request, location);
+  GetDocument().GetLayoutView()->Layer()->HitTest(location, result);
   EXPECT_EQ(overlap, result.InnerNode());
 
   // Same hit test, with stop node, should still hit 'overlap' because it's not
   // a descendant of 'hit'.
   request = HitTestRequest(HitTestRequest::kReadOnly | HitTestRequest::kActive,
                            hit->GetLayoutObject());
-  result = HitTestResult(request, LayoutPoint(50, 75));
-  GetDocument().GetLayoutView()->Layer()->HitTest(result);
+  result = HitTestResult(request, location);
+  GetDocument().GetLayoutView()->Layer()->HitTest(location, result);
   EXPECT_EQ(overlap, result.InnerNode());
 
   // List-based hit test with stop node
   request = HitTestRequest(HitTestRequest::kReadOnly | HitTestRequest::kActive |
                                HitTestRequest::kListBased,
                            hit->GetLayoutObject());
-  result = HitTestResult(request, LayoutRect(40, 15, 20, 20));
-  GetDocument().GetLayoutView()->Layer()->HitTest(result);
+  location = HitTestLocation((LayoutRect(40, 15, 20, 20)));
+  result = HitTestResult(request, location);
+  GetDocument().GetLayoutView()->Layer()->HitTest(location, result);
   EXPECT_EQ(1u, result.ListBasedTestResult().size());
   EXPECT_EQ(hit, *result.ListBasedTestResult().begin());
 }
@@ -1429,14 +1433,15 @@
   Element* table = GetDocument().getElementById("table");
   Element* cell11 = GetDocument().getElementById("cell11");
   HitTestRequest request(HitTestRequest::kReadOnly | HitTestRequest::kActive);
-  HitTestResult result(request, LayoutPoint(50, 50));
-  GetDocument().GetLayoutView()->Layer()->HitTest(result);
+  HitTestLocation location((LayoutPoint(50, 50)));
+  HitTestResult result(request, location);
+  GetDocument().GetLayoutView()->Layer()->HitTest(location, result);
   EXPECT_EQ(cell11, result.InnerNode());
 
   request = HitTestRequest(HitTestRequest::kReadOnly | HitTestRequest::kActive,
                            table->GetLayoutObject());
-  result = HitTestResult(request, LayoutPoint(50, 50));
-  GetDocument().GetLayoutView()->Layer()->HitTest(result);
+  result = HitTestResult(request, location);
+  GetDocument().GetLayoutView()->Layer()->HitTest(location, result);
   EXPECT_EQ(table, result.InnerNode());
 }
 
@@ -1449,14 +1454,15 @@
   Element* svg = GetDocument().getElementById("svg");
   Element* circle = GetDocument().getElementById("circle");
   HitTestRequest request(HitTestRequest::kReadOnly | HitTestRequest::kActive);
-  HitTestResult result(request, LayoutPoint(50, 50));
-  GetDocument().GetLayoutView()->Layer()->HitTest(result);
+  HitTestLocation location((LayoutPoint(50, 50)));
+  HitTestResult result(request, location);
+  GetDocument().GetLayoutView()->Layer()->HitTest(location, result);
   EXPECT_EQ(circle, result.InnerNode());
 
   request = HitTestRequest(HitTestRequest::kReadOnly | HitTestRequest::kActive,
                            svg->GetLayoutObject());
-  result = HitTestResult(request, LayoutPoint(50, 50));
-  GetDocument().GetLayoutView()->Layer()->HitTest(result);
+  result = HitTestResult(request, location);
+  GetDocument().GetLayoutView()->Layer()->HitTest(location, result);
   EXPECT_EQ(svg, result.InnerNode());
 }
 
diff --git a/third_party/blink/renderer/core/paint/paint_property_tree_builder.cc b/third_party/blink/renderer/core/paint/paint_property_tree_builder.cc
index f5568c7..d8c069f 100644
--- a/third_party/blink/renderer/core/paint/paint_property_tree_builder.cc
+++ b/third_party/blink/renderer/core/paint/paint_property_tree_builder.cc
@@ -2189,20 +2189,27 @@
     new_fragment_contexts.push_back(
         ContextForFragment(fragment_clip, logical_top_in_flow_thread));
 
-    base::Optional<LayoutUnit> old_logical_top_in_flow_thread;
     if (current_fragment_data) {
-      if (const auto* old_fragment = current_fragment_data->NextFragment())
-        old_logical_top_in_flow_thread = old_fragment->LogicalTopInFlowThread();
+      if (!current_fragment_data->NextFragment())
+        fragments_changed = true;
       current_fragment_data = &current_fragment_data->EnsureNextFragment();
     } else {
       current_fragment_data = &object_.GetMutableForPainting().FirstFragment();
-      old_logical_top_in_flow_thread =
-          current_fragment_data->LogicalTopInFlowThread();
     }
 
-    if (!old_logical_top_in_flow_thread ||
-        *old_logical_top_in_flow_thread != logical_top_in_flow_thread)
-      fragments_changed = true;
+    fragments_changed |= logical_top_in_flow_thread !=
+                         current_fragment_data->LogicalTopInFlowThread();
+    if (!fragments_changed) {
+      const ClipPaintPropertyNode* old_fragment_clip = nullptr;
+      if (const auto* properties = current_fragment_data->PaintProperties())
+        old_fragment_clip = properties->FragmentClip();
+      const base::Optional<LayoutRect>& new_fragment_clip =
+          new_fragment_contexts.back().fragment_clip;
+      fragments_changed =
+          !!old_fragment_clip != !!new_fragment_clip ||
+          (old_fragment_clip && new_fragment_clip &&
+           old_fragment_clip->ClipRect() != ToClipRect(*new_fragment_clip));
+    }
 
     InitFragmentPaintProperties(
         *current_fragment_data,
diff --git a/third_party/blink/renderer/core/paint/paint_property_tree_builder_test.cc b/third_party/blink/renderer/core/paint/paint_property_tree_builder_test.cc
index c13ecb5..4a65eaa 100644
--- a/third_party/blink/renderer/core/paint/paint_property_tree_builder_test.cc
+++ b/third_party/blink/renderer/core/paint/paint_property_tree_builder_test.cc
@@ -3591,25 +3591,6 @@
             nullptr);
 }
 
-static unsigned NumFragments(const LayoutObject* obj) {
-  unsigned count = 0;
-  auto* fragment = &obj->FirstFragment();
-  while (fragment) {
-    count++;
-    fragment = fragment->NextFragment();
-  }
-  return count;
-}
-
-static const FragmentData& FragmentAt(const LayoutObject* obj, unsigned count) {
-  auto* fragment = &obj->FirstFragment();
-  while (count > 0) {
-    count--;
-    fragment = fragment->NextFragment();
-  }
-  return *fragment;
-}
-
 TEST_P(PaintPropertyTreeBuilderTest, PaintOffsetsUnderMultiColumnScrolled) {
   SetBodyInnerHTML(R"HTML(
     <!doctype HTML>
diff --git a/third_party/blink/renderer/core/paint/paint_property_tree_builder_test.h b/third_party/blink/renderer/core/paint/paint_property_tree_builder_test.h
index b5c290c8..0166f288 100644
--- a/third_party/blink/renderer/core/paint/paint_property_tree_builder_test.h
+++ b/third_party/blink/renderer/core/paint/paint_property_tree_builder_test.h
@@ -40,6 +40,26 @@
 
   const ObjectPaintProperties* PaintPropertiesForElement(const char* name);
 
+  static unsigned NumFragments(const LayoutObject* obj) {
+    unsigned count = 0;
+    auto* fragment = &obj->FirstFragment();
+    while (fragment) {
+      count++;
+      fragment = fragment->NextFragment();
+    }
+    return count;
+  }
+
+  static const FragmentData& FragmentAt(const LayoutObject* obj,
+                                        unsigned count) {
+    auto* fragment = &obj->FirstFragment();
+    while (count > 0) {
+      count--;
+      fragment = fragment->NextFragment();
+    }
+    return *fragment;
+  }
+
  private:
   void SetUp() override;
 };
diff --git a/third_party/blink/renderer/core/paint/paint_property_tree_update_tests.cc b/third_party/blink/renderer/core/paint/paint_property_tree_update_tests.cc
index bd575e1..5026f51 100644
--- a/third_party/blink/renderer/core/paint/paint_property_tree_update_tests.cc
+++ b/third_party/blink/renderer/core/paint/paint_property_tree_update_tests.cc
@@ -1175,4 +1175,50 @@
   EXPECT_FALSE(PaintPropertiesForElement("target")->OverflowClip());
 }
 
+TEST_P(PaintPropertyTreeUpdateTest,
+       FragmentClipUpdateOnMulticolContainerWidthChange) {
+  SetBodyInnerHTML(R"HTML(
+    <style>body {margin: 0}</style>
+    <div id="container" style="width: 100px">
+      <div id="multicol" style="columns: 2; column-gap: 0; line-height: 500px">
+        <div><br></div>
+        <div><br></div>
+      </div>
+    </div>
+  )HTML");
+
+  auto* flow_thread = GetLayoutObjectByElementId("multicol")->SlowFirstChild();
+  ASSERT_EQ(2u, NumFragments(flow_thread));
+  EXPECT_EQ(50, FragmentAt(flow_thread, 0)
+                    .PaintProperties()
+                    ->FragmentClip()
+                    ->ClipRect()
+                    .Rect()
+                    .MaxX());
+  EXPECT_EQ(50, FragmentAt(flow_thread, 1)
+                    .PaintProperties()
+                    ->FragmentClip()
+                    ->ClipRect()
+                    .Rect()
+                    .X());
+
+  GetDocument()
+      .getElementById("container")
+      ->setAttribute(HTMLNames::styleAttr, "width: 500px");
+  GetDocument().View()->UpdateAllLifecyclePhases();
+  ASSERT_EQ(2u, NumFragments(flow_thread));
+  EXPECT_EQ(250, FragmentAt(flow_thread, 0)
+                     .PaintProperties()
+                     ->FragmentClip()
+                     ->ClipRect()
+                     .Rect()
+                     .MaxX());
+  EXPECT_EQ(250, FragmentAt(flow_thread, 1)
+                     .PaintProperties()
+                     ->FragmentClip()
+                     ->ClipRect()
+                     .Rect()
+                     .X());
+}
+
 }  // namespace blink
diff --git a/third_party/blink/renderer/core/testing/core_unit_test_helper.cc b/third_party/blink/renderer/core/testing/core_unit_test_helper.cc
index 97ab35e..b86cc17 100644
--- a/third_party/blink/renderer/core/testing/core_unit_test_helper.cc
+++ b/third_party/blink/renderer/core/testing/core_unit_test_helper.cc
@@ -42,11 +42,12 @@
     : UseMockScrollbarSettings(), local_frame_client_(local_frame_client) {}
 
 const Node* RenderingTest::HitTest(int x, int y) {
+  HitTestLocation location(LayoutPoint(x, y));
   HitTestResult result(
       HitTestRequest(HitTestRequest::kReadOnly | HitTestRequest::kActive |
                      HitTestRequest::kAllowChildFrameContent),
-      IntPoint(x, y));
-  GetLayoutView().HitTest(result);
+      location);
+  GetLayoutView().HitTest(location, result);
   return result.InnerNode();
 }
 
diff --git a/third_party/blink/renderer/core/testing/internals.cc b/third_party/blink/renderer/core/testing/internals.cc
index 23a0345..8766e4d 100644
--- a/third_party/blink/renderer/core/testing/internals.cc
+++ b/third_party/blink/renderer/core/testing/internals.cc
@@ -1416,7 +1416,8 @@
 // FIXME: The next four functions are very similar - combine them once
 // bestClickableNode/bestContextMenuNode have been combined..
 
-void Internals::HitTestRect(HitTestResult& result,
+void Internals::HitTestRect(HitTestLocation& location,
+                            HitTestResult& result,
                             long x,
                             long y,
                             long width,
@@ -1426,10 +1427,11 @@
   EventHandler& event_handler = document->GetFrame()->GetEventHandler();
   LayoutPoint hit_test_point(
       document->GetFrame()->View()->ConvertFromRootFrame(LayoutPoint(x, y)));
-  result = event_handler.HitTestResultAtRect(
-      LayoutRect(hit_test_point, LayoutSize((int)width, (int)height)),
-      HitTestRequest::kReadOnly | HitTestRequest::kActive |
-          HitTestRequest::kListBased);
+  location = HitTestLocation(
+      (LayoutRect(hit_test_point, LayoutSize((int)width, (int)height))));
+  result = event_handler.HitTestResultAtLocation(
+      location, HitTestRequest::kReadOnly | HitTestRequest::kActive |
+                    HitTestRequest::kListBased);
 }
 
 DOMPoint* Internals::touchPositionAdjustedToBestClickableNode(
@@ -1446,14 +1448,15 @@
     return nullptr;
   }
 
+  HitTestLocation location;
   HitTestResult result;
-  HitTestRect(result, x, y, width, height, document);
+  HitTestRect(location, result, x, y, width, height, document);
   Node* target_node = nullptr;
   IntPoint adjusted_point;
 
   EventHandler& event_handler = document->GetFrame()->GetEventHandler();
   bool found_node = event_handler.BestClickableNodeForHitTestResult(
-      result, adjusted_point, target_node);
+      location, result, adjusted_point, target_node);
   if (found_node)
     return DOMPoint::Create(adjusted_point.X(), adjusted_point.Y());
 
@@ -1474,12 +1477,13 @@
     return nullptr;
   }
 
+  HitTestLocation location;
   HitTestResult result;
-  HitTestRect(result, x, y, width, height, document);
+  HitTestRect(location, result, x, y, width, height, document);
   Node* target_node = nullptr;
   IntPoint adjusted_point;
   document->GetFrame()->GetEventHandler().BestClickableNodeForHitTestResult(
-      result, adjusted_point, target_node);
+      location, result, adjusted_point, target_node);
   return target_node;
 }
 
@@ -1497,14 +1501,15 @@
     return nullptr;
   }
 
+  HitTestLocation location;
   HitTestResult result;
-  HitTestRect(result, x, y, width, height, document);
+  HitTestRect(location, result, x, y, width, height, document);
   Node* target_node = nullptr;
   IntPoint adjusted_point;
 
   EventHandler& event_handler = document->GetFrame()->GetEventHandler();
   bool found_node = event_handler.BestContextMenuNodeForHitTestResult(
-      result, adjusted_point, target_node);
+      location, result, adjusted_point, target_node);
   if (found_node)
     return DOMPoint::Create(adjusted_point.X(), adjusted_point.Y());
 
@@ -1525,12 +1530,13 @@
     return nullptr;
   }
 
+  HitTestLocation location;
   HitTestResult result;
-  HitTestRect(result, x, y, width, height, document);
+  HitTestRect(location, result, x, y, width, height, document);
   Node* target_node = nullptr;
   IntPoint adjusted_point;
   document->GetFrame()->GetEventHandler().BestContextMenuNodeForHitTestResult(
-      result, adjusted_point, target_node);
+      location, result, adjusted_point, target_node);
   return target_node;
 }
 
@@ -1997,8 +2003,9 @@
 
   HeapVector<Member<Node>> matches;
   HitTestRequest request(hit_type);
-  HitTestResult result(request, rect);
-  frame->ContentLayoutObject()->HitTest(result);
+  HitTestLocation location(rect);
+  HitTestResult result(request, location);
+  frame->ContentLayoutObject()->HitTest(location, result);
   CopyToVector(result.ListBasedTestResult(), matches);
 
   return StaticNodeList::Adopt(matches);
diff --git a/third_party/blink/renderer/core/testing/internals.h b/third_party/blink/renderer/core/testing/internals.h
index 80da0099..6df34037 100644
--- a/third_party/blink/renderer/core/testing/internals.h
+++ b/third_party/blink/renderer/core/testing/internals.h
@@ -57,6 +57,7 @@
 class ExceptionState;
 class ExecutionContext;
 class GCObservation;
+class HitTestLocation;
 class HitTestResult;
 class HTMLInputElement;
 class HTMLMediaElement;
@@ -592,7 +593,8 @@
   LocalFrame* GetFrame() const;
   Vector<String> IconURLs(Document*, int icon_types_mask) const;
   DOMRectList* AnnotatedRegions(Document*, bool draggable, ExceptionState&);
-  void HitTestRect(HitTestResult&,
+  void HitTestRect(HitTestLocation&,
+                   HitTestResult&,
                    long x,
                    long y,
                    long width,
diff --git a/third_party/blink/renderer/devtools/front_end/bindings/NetworkProject.js b/third_party/blink/renderer/devtools/front_end/bindings/NetworkProject.js
index 50c7d67..cddee9d 100644
--- a/third_party/blink/renderer/devtools/front_end/bindings/NetworkProject.js
+++ b/third_party/blink/renderer/devtools/front_end/bindings/NetworkProject.js
@@ -106,6 +106,8 @@
    */
   static removeFrameAttribution(uiSourceCode, frameId) {
     const frameAttribution = uiSourceCode[Bindings.NetworkProject._frameAttributionSymbol];
+    if (!frameAttribution)
+      return;
     const attributionInfo = frameAttribution.get(frameId);
     console.assert(attributionInfo, 'Failed to remove frame attribution for url: ' + uiSourceCode.url());
     attributionInfo.count -= 1;
diff --git a/third_party/blink/renderer/devtools/front_end/inspector_main/InspectorMain.js b/third_party/blink/renderer/devtools/front_end/inspector_main/InspectorMain.js
index 7a60d01..02a9c1ca 100644
--- a/third_party/blink/renderer/devtools/front_end/inspector_main/InspectorMain.js
+++ b/third_party/blink/renderer/devtools/front_end/inspector_main/InspectorMain.js
@@ -34,6 +34,8 @@
     const target = SDK.targetManager.createTarget(
         'main', Common.UIString('Main'), this._capabilitiesForMainTarget(), this._createMainConnection.bind(this),
         null);
+    if (Runtime.queryParam('v8only'))
+      target.markAsNodeJS();
     target.runtimeAgent().runIfWaitingForDebugger();
   }
 
diff --git a/third_party/blink/renderer/devtools/front_end/js_main/JsMain.js b/third_party/blink/renderer/devtools/front_end/js_main/JsMain.js
index 8b99080..229e339 100644
--- a/third_party/blink/renderer/devtools/front_end/js_main/JsMain.js
+++ b/third_party/blink/renderer/devtools/front_end/js_main/JsMain.js
@@ -13,6 +13,7 @@
     Host.userMetrics.actionTaken(Host.UserMetrics.Action.ConnectToNodeJSDirectly);
     const target = SDK.targetManager.createTarget(
         'main', Common.UIString('Main'), SDK.Target.Capability.JS, this._createMainConnection.bind(this), null);
+    target.markAsNodeJS();
     target.runtimeAgent().runIfWaitingForDebugger();
     InspectorFrontendHost.connectionReady();
   }
diff --git a/third_party/blink/renderer/devtools/front_end/node_main/NodeMain.js b/third_party/blink/renderer/devtools/front_end/node_main/NodeMain.js
index 28dc0b4..0afea822 100644
--- a/third_party/blink/renderer/devtools/front_end/node_main/NodeMain.js
+++ b/third_party/blink/renderer/devtools/front_end/node_main/NodeMain.js
@@ -100,6 +100,7 @@
     const target = this._targetManager.createTarget(
         targetInfo.targetId, Common.UIString('Node.js: %s', targetInfo.url), SDK.Target.Capability.JS,
         this._createChildConnection.bind(this, this._targetAgent, sessionId), this._parentTarget);
+    target.markAsNodeJS();
     target.runtimeAgent().runIfWaitingForDebugger();
   }
 
diff --git a/third_party/blink/renderer/devtools/front_end/profiler/IsolateSelector.js b/third_party/blink/renderer/devtools/front_end/profiler/IsolateSelector.js
index 397206fe..c334c48 100644
--- a/third_party/blink/renderer/devtools/front_end/profiler/IsolateSelector.js
+++ b/third_party/blink/renderer/devtools/front_end/profiler/IsolateSelector.js
@@ -21,6 +21,7 @@
     this._isolateByModel = new Map();
     /** @type {!Map<string, !Profiler.IsolateSelector.ListItem>} */
     this._itemByIsolate = new Map();
+    this._updateTimer = null;
 
     SDK.targetManager.observeModels(SDK.RuntimeModel, this);
     SDK.targetManager.addEventListener(SDK.TargetManager.Events.NameChanged, this._targetChanged, this);
@@ -29,6 +30,20 @@
 
   /**
    * @override
+   */
+  wasShown() {
+    this._updateStats();
+  }
+
+  /**
+   * @override
+   */
+  willHide() {
+    clearTimeout(this._updateTimer);
+  }
+
+  /**
+   * @override
    * @param {!SDK.RuntimeModel} model
    */
   modelAdded(model) {
@@ -137,6 +152,13 @@
   _update() {
     this._list.invalidateRange(0, this._items.length);
   }
+
+  _updateStats() {
+    for (const item of this._itemByIsolate.values())
+      item.updateStats();
+    const heapStatsUpdateIntervalMs = 2000;
+    this._updateTimer = setTimeout(() => this._updateStats(), heapStatsUpdateIntervalMs);
+  }
 };
 
 Profiler.IsolateSelector.ListItem = class {
@@ -149,9 +171,9 @@
     this.element = createElementWithClass('div', 'profile-isolate-item hbox');
     this._heapDiv = this.element.createChild('div', 'profile-isolate-item-heap');
     this._nameDiv = this.element.createChild('div', 'profile-isolate-item-name');
-    this._updateTimer = null;
+    this._updatesDisabled = false;
     this.updateTitle();
-    this._updateStats();
+    this.updateStats();
   }
 
   /**
@@ -168,9 +190,6 @@
   removeModel(model) {
     this._models.delete(model);
     this.updateTitle();
-    if (this._models.size)
-      return;
-    clearTimeout(this._updateTimer);
   }
 
   /**
@@ -180,10 +199,14 @@
     return Array.from(this._models);
   }
 
-  async _updateStats() {
-    const heapStats = await this._models.values().next().value.heapUsage();
-    if (!heapStats)
+  async updateStats() {
+    if (this._updatesDisabled)
       return;
+    const heapStats = await this._models.values().next().value.heapUsage();
+    if (!heapStats) {
+      this._updatesDisabled = true;
+      return;
+    }
     const usedTitle = ls`Heap size in use by live JS objects.`;
     const totalTitle = ls`Total JS heap size including live objects, garbage, and reserved space.`;
     this._heapDiv.removeChildren();
@@ -206,10 +229,8 @@
     }
     this._nameDiv.removeChildren();
     for (const [name, count] of modelCountByName) {
-      const lineDiv = this._nameDiv.createChild('div');
       const title = count > 1 ? `${name} (${count})` : name;
-      lineDiv.textContent = title;
-      lineDiv.setAttribute('title', title);
+      this._nameDiv.appendChild(UI.html`<div title="${title}">${title}</div>`);
     }
   }
 };
diff --git a/third_party/blink/renderer/devtools/front_end/sdk/Target.js b/third_party/blink/renderer/devtools/front_end/sdk/Target.js
index 87cc1c6a..42b8d500 100644
--- a/third_party/blink/renderer/devtools/front_end/sdk/Target.js
+++ b/third_party/blink/renderer/devtools/front_end/sdk/Target.js
@@ -46,12 +46,11 @@
    * @return {boolean}
    */
   isNodeJS() {
-    // TODO(lushnikov): this is an unreliable way to detect Node.js targets.
-    return this._capabilitiesMask === SDK.Target.Capability.JS || this._isNodeJSForTest;
+    return this._isNodeJS;
   }
 
-  setIsNodeJSForTest() {
-    this._isNodeJSForTest = true;
+  markAsNodeJS() {
+    this._isNodeJS = true;
   }
 
   /**
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 0dfce79..61ed37a8 100644
--- a/third_party/blink/renderer/modules/accessibility/ax_layout_object.cc
+++ b/third_party/blink/renderer/modules/accessibility/ax_layout_object.cc
@@ -1663,8 +1663,9 @@
   PaintLayer* layer = ToLayoutBox(layout_object_)->Layer();
 
   HitTestRequest request(HitTestRequest::kReadOnly | HitTestRequest::kActive);
-  HitTestResult hit_test_result = HitTestResult(request, point);
-  layer->HitTest(hit_test_result);
+  HitTestLocation location(point);
+  HitTestResult hit_test_result = HitTestResult(request, location);
+  layer->HitTest(location, hit_test_result);
 
   Node* node = hit_test_result.InnerNode();
   if (!node)
diff --git a/third_party/blink/renderer/platform/blob/blob_data.cc b/third_party/blink/renderer/platform/blob/blob_data.cc
index ba69654..543574f 100644
--- a/third_party/blink/renderer/platform/blob/blob_data.cc
+++ b/third_party/blink/renderer/platform/blob/blob_data.cc
@@ -362,6 +362,18 @@
   return blob_clone;
 }
 
+network::mojom::blink::DataPipeGetterPtr BlobDataHandle::AsDataPipeGetter() {
+  MutexLocker locker(blob_info_mutex_);
+  if (!blob_info_.is_valid())
+    return nullptr;
+  network::mojom::blink::DataPipeGetterPtr result;
+  BlobPtr blob;
+  blob.Bind(std::move(blob_info_));
+  blob->AsDataPipeGetter(MakeRequest(&result));
+  blob_info_ = blob.PassInterface();
+  return result;
+}
+
 void BlobDataHandle::ReadAll(mojo::ScopedDataPipeProducerHandle pipe,
                              mojom::blink::BlobReaderClientPtr client) {
   MutexLocker locker(blob_info_mutex_);
diff --git a/third_party/blink/renderer/platform/blob/blob_data.h b/third_party/blink/renderer/platform/blob/blob_data.h
index 773bcf6..e51d4c05 100644
--- a/third_party/blink/renderer/platform/blob/blob_data.h
+++ b/third_party/blink/renderer/platform/blob/blob_data.h
@@ -192,6 +192,7 @@
   ~BlobDataHandle();
 
   mojom::blink::BlobPtr CloneBlobPtr();
+  network::mojom::blink::DataPipeGetterPtr AsDataPipeGetter();
 
   void ReadAll(mojo::ScopedDataPipeProducerHandle,
                mojom::blink::BlobReaderClientPtr);
diff --git a/third_party/blink/renderer/platform/blob/blob_registry.h b/third_party/blink/renderer/platform/blob/blob_registry.h
index 56d2fcb0..13cd04d 100644
--- a/third_party/blink/renderer/platform/blob/blob_registry.h
+++ b/third_party/blink/renderer/platform/blob/blob_registry.h
@@ -38,7 +38,6 @@
 
 namespace blink {
 
-class BlobBytesConsumer;
 class BlobDataHandle;
 class BlobURLRegistry;
 class KURL;
@@ -49,12 +48,11 @@
   STATIC_ONLY(BlobRegistry);
 
   // Calling methods in this class directly won't work when Blob URL management
-  // is switched to mojo. Instead codew should call PublicURLManager methods to
+  // is switched to mojo. Instead code should call PublicURLManager methods to
   // create/revoke blob URLs.
   // To avoid new usage of these methods, mark all as private with friends for
   // existing usage.
  private:
-  friend class BlobBytesConsumer;
   friend class BlobURLRegistry;
 
   // Methods for controlling Blob URLs.
diff --git a/third_party/blink/renderer/platform/blob/testing/fake_blob.cc b/third_party/blink/renderer/platform/blob/testing/fake_blob.cc
index 096cae3..f8e69fd 100644
--- a/third_party/blink/renderer/platform/blob/testing/fake_blob.cc
+++ b/third_party/blink/renderer/platform/blob/testing/fake_blob.cc
@@ -5,19 +5,52 @@
 #include "third_party/blink/renderer/platform/blob/testing/fake_blob.h"
 
 #include "mojo/public/cpp/bindings/strong_binding.h"
+#include "mojo/public/cpp/system/data_pipe_utils.h"
+#include "third_party/blink/public/platform/web_string.h"
 
 namespace blink {
+namespace {
 
-FakeBlob::FakeBlob(const String& uuid) : uuid_(uuid) {}
+class SimpleDataPipeGetter : public network::mojom::blink::DataPipeGetter {
+ public:
+  SimpleDataPipeGetter(const String& str) : str_(str) {}
+  ~SimpleDataPipeGetter() override = default;
+
+  // network::mojom::DataPipeGetter implementation:
+  void Read(mojo::ScopedDataPipeProducerHandle handle,
+            ReadCallback callback) override {
+    std::move(callback).Run(0 /* OK */, str_.length());
+    bool result = mojo::BlockingCopyFromString(WebString(str_).Utf8(), handle);
+    DCHECK(result);
+  }
+
+  void Clone(network::mojom::blink::DataPipeGetterRequest request) override {
+    mojo::MakeStrongBinding(std::make_unique<SimpleDataPipeGetter>(str_),
+                            std::move(request));
+  }
+
+ private:
+  String str_;
+
+  DISALLOW_COPY_AND_ASSIGN(SimpleDataPipeGetter);
+};
+
+}  // namespace
+
+FakeBlob::FakeBlob(const String& uuid, const String& body, State* state)
+    : uuid_(uuid), body_(body), state_(state) {}
 
 void FakeBlob::Clone(mojom::blink::BlobRequest request) {
-  mojo::MakeStrongBinding(std::make_unique<FakeBlob>(uuid_),
+  mojo::MakeStrongBinding(std::make_unique<FakeBlob>(uuid_, body_, state_),
                           std::move(request));
 }
 
 void FakeBlob::AsDataPipeGetter(
     network::mojom::blink::DataPipeGetterRequest request) {
-  NOTREACHED();
+  if (state_)
+    state_->did_initiate_read_operation = true;
+  mojo::MakeStrongBinding(std::make_unique<SimpleDataPipeGetter>(body_),
+                          std::move(request));
 }
 
 void FakeBlob::ReadRange(uint64_t offset,
@@ -27,9 +60,16 @@
   NOTREACHED();
 }
 
-void FakeBlob::ReadAll(mojo::ScopedDataPipeProducerHandle,
-                       mojom::blink::BlobReaderClientPtr) {
-  NOTREACHED();
+void FakeBlob::ReadAll(mojo::ScopedDataPipeProducerHandle handle,
+                       mojom::blink::BlobReaderClientPtr client) {
+  if (state_)
+    state_->did_initiate_read_operation = true;
+  if (client)
+    client->OnCalculatedSize(body_.length(), body_.length());
+  bool result = mojo::BlockingCopyFromString(WebString(body_).Utf8(), handle);
+  DCHECK(result);
+  if (client)
+    client->OnComplete(0 /* OK */, body_.length());
 }
 
 void FakeBlob::ReadSideData(ReadSideDataCallback callback) {
diff --git a/third_party/blink/renderer/platform/blob/testing/fake_blob.h b/third_party/blink/renderer/platform/blob/testing/fake_blob.h
index 447645fa..6013df81 100644
--- a/third_party/blink/renderer/platform/blob/testing/fake_blob.h
+++ b/third_party/blink/renderer/platform/blob/testing/fake_blob.h
@@ -9,11 +9,17 @@
 
 namespace blink {
 
-// Mocked Blob implementation for testing. You can't read from a FakeBlob, but
-// it does have a UUID.
+// Mocked Blob implementation for testing. Implements all methods except for
+// ReadRange and ReadSideData.
 class FakeBlob : public mojom::blink::Blob {
  public:
-  explicit FakeBlob(const String& uuid);
+  struct State {
+    bool did_initiate_read_operation = false;
+  };
+
+  FakeBlob(const String& uuid,
+           const String& body = String(),
+           State* state = nullptr);
 
   void Clone(mojom::blink::BlobRequest) override;
   void AsDataPipeGetter(network::mojom::blink::DataPipeGetterRequest) override;
@@ -25,9 +31,10 @@
                mojom::blink::BlobReaderClientPtr) override;
   void ReadSideData(ReadSideDataCallback) override;
   void GetInternalUUID(GetInternalUUIDCallback) override;
-
  private:
   String uuid_;
+  String body_;
+  State* state_;
 };
 
 }  // namespace blink
diff --git a/third_party/blink/renderer/platform/geometry/float_point.cc b/third_party/blink/renderer/platform/geometry/float_point.cc
index fda6393..75a820a 100644
--- a/third_party/blink/renderer/platform/geometry/float_point.cc
+++ b/third_party/blink/renderer/platform/geometry/float_point.cc
@@ -116,10 +116,6 @@
   return gfx::ScrollOffset(x_, y_);
 }
 
-FloatPoint::operator gfx::Vector2dF() const {
-  return gfx::Vector2dF(x_, y_);
-}
-
 std::ostream& operator<<(std::ostream& ostream, const FloatPoint& point) {
   return ostream << point.ToString();
 }
diff --git a/third_party/blink/renderer/platform/geometry/float_point.h b/third_party/blink/renderer/platform/geometry/float_point.h
index acca3168..fcabd694 100644
--- a/third_party/blink/renderer/platform/geometry/float_point.h
+++ b/third_party/blink/renderer/platform/geometry/float_point.h
@@ -47,7 +47,6 @@
 namespace gfx {
 class PointF;
 class ScrollOffset;
-class Vector2dF;
 }
 
 namespace blink {
@@ -136,7 +135,6 @@
 
   operator gfx::PointF() const;
   explicit operator gfx::ScrollOffset() const;
-  explicit operator gfx::Vector2dF() const;
 
   String ToString() const;
 
diff --git a/third_party/blink/renderer/platform/graphics/filters/source_graphic.cc b/third_party/blink/renderer/platform/graphics/filters/source_graphic.cc
index d06b534d..bb5d23d7 100644
--- a/third_party/blink/renderer/platform/graphics/filters/source_graphic.cc
+++ b/third_party/blink/renderer/platform/graphics/filters/source_graphic.cc
@@ -36,7 +36,7 @@
 }
 
 FloatRect SourceGraphic::MapInputs(const FloatRect& rect) const {
-  return !source_rect_.IsEmpty() ? source_rect_ : rect;
+  return !source_rect_.IsEmpty() ? FloatRect(source_rect_) : rect;
 }
 
 void SourceGraphic::SetSourceRect(const IntRect& source_rect) {
diff --git a/third_party/blink/renderer/platform/graphics/graphics_layer.cc b/third_party/blink/renderer/platform/graphics/graphics_layer.cc
index 40395ce..52587e0a 100644
--- a/third_party/blink/renderer/platform/graphics/graphics_layer.cc
+++ b/third_party/blink/renderer/platform/graphics/graphics_layer.cc
@@ -286,7 +286,7 @@
   PaintRecursivelyInternal(repainted_layers);
 
   // Notify the controllers that the artifact has been pushed and some
-  // lifecycle state can be updated.
+  // lifecycle state can be freed (such as raster invalidations).
   for (auto* layer : repainted_layers) {
 #if DCHECK_IS_ON()
     if (VLOG_IS_ON(2))
@@ -335,13 +335,10 @@
     return false;
 #endif
 
-  bool repainted = false;
-  if (PaintWithoutCommit(interest_rect, disabled_mode)) {
-    repainted = true;
+  if (PaintWithoutCommit(interest_rect, disabled_mode))
     GetPaintController().CommitNewDisplayItems();
-  } else if (!needs_check_raster_invalidation_) {
+  else if (!needs_check_raster_invalidation_)
     return false;
-  }
 
 #if DCHECK_IS_ON()
   if (VLOG_IS_ON(2)) {
@@ -372,7 +369,7 @@
   }
 
   needs_check_raster_invalidation_ = false;
-  return repainted;
+  return true;
 }
 
 bool GraphicsLayer::PaintWithoutCommit(
diff --git a/third_party/blink/renderer/platform/graphics/paint/cull_rect_test.cc b/third_party/blink/renderer/platform/graphics/paint/cull_rect_test.cc
index c543a27..2690dd8 100644
--- a/third_party/blink/renderer/platform/graphics/paint/cull_rect_test.cc
+++ b/third_party/blink/renderer/platform/graphics/paint/cull_rect_test.cc
@@ -36,7 +36,7 @@
   AffineTransform transform;
   transform.Translate(-2, -2);
 
-  EXPECT_TRUE(cull_rect.IntersectsCullRect(transform, IntRect(51, 51, 1, 1)));
+  EXPECT_TRUE(cull_rect.IntersectsCullRect(transform, FloatRect(51, 51, 1, 1)));
   EXPECT_FALSE(cull_rect.IntersectsCullRect(IntRect(52, 52, 1, 1)));
 }
 
diff --git a/third_party/blink/renderer/platform/graphics/paint/paint_controller.cc b/third_party/blink/renderer/platform/graphics/paint/paint_controller.cc
index 1a42f070..178d297 100644
--- a/third_party/blink/renderer/platform/graphics/paint/paint_controller.cc
+++ b/third_party/blink/renderer/platform/graphics/paint/paint_controller.cc
@@ -497,6 +497,7 @@
 #endif
 
   cache_is_all_invalid_ = false;
+  committed_ = true;
 
   new_cached_subsequences_.swap(current_cached_subsequences_);
   new_cached_subsequences_.clear();
@@ -528,21 +529,25 @@
   DCHECK(new_display_item_list_.IsEmpty());
   DCHECK(new_paint_chunks_.IsInInitialState());
 
-  // Validate display item clients that have validly cached subsequence or
-  // display items in this PaintController.
-  for (auto& item : current_cached_subsequences_) {
-    if (item.key->IsCacheable())
-      item.key->Validate();
-  }
-  for (const auto& item : current_paint_artifact_->GetDisplayItemList()) {
-    const auto& client = item.Client();
-    client.ClearPartialInvalidationVisualRect();
-    if (client.IsCacheable())
-      client.Validate();
-  }
-  for (const auto& chunk : current_paint_artifact_->PaintChunks()) {
-    if (chunk.id.client.IsCacheable())
-      chunk.id.client.Validate();
+  if (committed_) {
+    committed_ = false;
+
+    // Validate display item clients that have validly cached subsequence or
+    // display items in this PaintController.
+    for (auto& item : current_cached_subsequences_) {
+      if (item.key->IsCacheable())
+        item.key->Validate();
+    }
+    for (const auto& item : current_paint_artifact_->GetDisplayItemList()) {
+      const auto& client = item.Client();
+      client.ClearPartialInvalidationVisualRect();
+      if (client.IsCacheable())
+        client.Validate();
+    }
+    for (const auto& chunk : current_paint_artifact_->PaintChunks()) {
+      if (chunk.id.client.IsCacheable())
+        chunk.id.client.Validate();
+    }
   }
 
   current_paint_artifact_->FinishCycle();
diff --git a/third_party/blink/renderer/platform/graphics/paint/paint_controller.h b/third_party/blink/renderer/platform/graphics/paint/paint_controller.h
index d4ca5b14..c64e6fb 100644
--- a/third_party/blink/renderer/platform/graphics/paint/paint_controller.h
+++ b/third_party/blink/renderer/platform/graphics/paint/paint_controller.h
@@ -347,6 +347,7 @@
   bool subsequence_caching_disabled_ = false;
 
   bool cache_is_all_invalid_ = true;
+  bool committed_ = false;
 
   // A stack recording current frames' first paints.
   Vector<FrameFirstPaint> frame_first_paints_;
diff --git a/third_party/blink/renderer/platform/graphics/paint/raster_invalidator_test.cc b/third_party/blink/renderer/platform/graphics/paint/raster_invalidator_test.cc
index e02ca6dd..7633400 100644
--- a/third_party/blink/renderer/platform/graphics/paint/raster_invalidator_test.cc
+++ b/third_party/blink/renderer/platform/graphics/paint/raster_invalidator_test.cc
@@ -358,13 +358,13 @@
   ASSERT_EQ(4u, invalidations.size());
   // |clip1| change should trigger incremental raster invalidation.
   EXPECT_INCREMENTAL_INVALIDATION(invalidations, 0, artifact->PaintChunks()[1],
-                                  IntRect(-1000, -1000, 2000, 500));
+                                  FloatRect(-1000, -1000, 2000, 500));
   EXPECT_INCREMENTAL_INVALIDATION(invalidations, 1, artifact->PaintChunks()[1],
-                                  IntRect(-1000, -500, 500, 1000));
+                                  FloatRect(-1000, -500, 500, 1000));
   EXPECT_INCREMENTAL_INVALIDATION(invalidations, 2, artifact->PaintChunks()[1],
-                                  IntRect(500, -500, 500, 1000));
+                                  FloatRect(500, -500, 500, 1000));
   EXPECT_INCREMENTAL_INVALIDATION(invalidations, 3, artifact->PaintChunks()[1],
-                                  IntRect(-1000, 500, 2000, 500));
+                                  FloatRect(-1000, 500, 2000, 500));
   invalidator.SetTracksRasterInvalidations(false);
   FinishCycle(*artifact);
 
@@ -380,7 +380,7 @@
   ASSERT_EQ(1u, invalidations1.size());
   // |clip1| change should trigger incremental raster invalidation.
   EXPECT_INCREMENTAL_INVALIDATION(invalidations1, 0, artifact->PaintChunks()[1],
-                                  IntRect(500, -500, 500, 1000));
+                                  FloatRect(500, -500, 500, 1000));
   invalidator.SetTracksRasterInvalidations(false);
   FinishCycle(*artifact);
 }
diff --git a/third_party/blink/renderer/platform/graphics/static_bitmap_image.cc b/third_party/blink/renderer/platform/graphics/static_bitmap_image.cc
index 52f6a8d..14dc7cf8 100644
--- a/third_party/blink/renderer/platform/graphics/static_bitmap_image.cc
+++ b/third_party/blink/renderer/platform/graphics/static_bitmap_image.cc
@@ -91,12 +91,6 @@
   if (SkColorSpace::Equals(src_color_space.get(), dst_color_space.get()))
     return this;
 
-  // crbug.com/844145: Remove this when GPU-backed SkImage supports color
-  // covnersion for kRespect TransferFnBehavior (skia:6553).
-  if (skia_image->isTextureBacked() &&
-      transfer_function_behavior == SkTransferFunctionBehavior::kRespect) {
-    skia_image = skia_image->makeNonTextureImage();
-  }
   sk_sp<SkImage> converted_skia_image =
       skia_image->makeColorSpace(dst_color_space, transfer_function_behavior);
   DCHECK(converted_skia_image.get());
diff --git a/third_party/blink/renderer/platform/json/json_parser.cc b/third_party/blink/renderer/platform/json/json_parser.cc
index 28586ed..71be020 100644
--- a/third_party/blink/renderer/platform/json/json_parser.cc
+++ b/third_party/blink/renderer/platform/json/json_parser.cc
@@ -215,9 +215,8 @@
         default:
           return Error::kInvalidEscape;
       }
-    } else if (c == '\n') {
-      cursor->line++;
-      cursor->line_start = cursor->pos;
+    } else if (c < 0x20) {
+      return Error::kSyntaxError;
     } else if ('"' == c) {
       return Error::kNoError;
     }
@@ -278,7 +277,7 @@
       cursor->line++;
       ++(cursor->pos);
       cursor->line_start = cursor->pos;
-    } else if (IsSpaceOrNewline(c)) {
+    } else if (c == ' ' || c == '\n' || c == '\r' || c == '\t') {
       ++(cursor->pos);
     } else if (c == '/') {
       Error error = SkipComment(cursor, end);
diff --git a/third_party/blink/renderer/platform/json/json_parser_test.cc b/third_party/blink/renderer/platform/json/json_parser_test.cc
index c60ad7b..f07f78b 100644
--- a/third_party/blink/renderer/platform/json/json_parser_test.cc
+++ b/third_party/blink/renderer/platform/json/json_parser_test.cc
@@ -268,11 +268,6 @@
   EXPECT_EQ(JSONValue::kTypeString, root->GetType());
   EXPECT_TRUE(root->AsString(&str_val));
   EXPECT_EQ(" \"\\/\b\f\n\r\t\v", str_val);
-  root = ParseJSON("\"\n\"");
-  ASSERT_TRUE(root.get());
-  EXPECT_EQ(JSONValue::kTypeString, root->GetType());
-  EXPECT_TRUE(root->AsString(&str_val));
-  EXPECT_EQ("\n", str_val);
 
   // Test hex and unicode escapes including the null character.
   root = ParseJSON("\"\\x41\\x00\\u1234\"", &error);
@@ -296,10 +291,33 @@
   EXPECT_FALSE(root.get());
   EXPECT_EQ("Line: 1, column: 4, Unexpected data after root element.",
             error.message);
-  root = ParseJSON("\"string with \n new \n lines in it\"extra data", &error);
+
+  // Bare control characters (including newlines) are not permitted in string
+  // literals.
+  root = ParseJSON("\"\n\"", &error);
   EXPECT_FALSE(root.get());
-  EXPECT_EQ("Line: 3, column: 14, Unexpected data after root element.",
-            error.message);
+  EXPECT_EQ("Line: 1, column: 3, Syntax error.", error.message);
+  root = ParseJSON("[\"\n\"]", &error);
+  EXPECT_FALSE(root.get());
+  EXPECT_EQ("Line: 1, column: 4, Syntax error.", error.message);
+  root = ParseJSON("{\"\n\": true}", &error);
+  EXPECT_FALSE(root.get());
+  EXPECT_EQ("Line: 1, column: 4, Syntax error.", error.message);
+  root = ParseJSON("{\"key\": \"\n\"}", &error);
+  EXPECT_FALSE(root.get());
+  EXPECT_EQ("Line: 1, column: 11, Syntax error.", error.message);
+  root = ParseJSON("\"\x1b\"", &error);
+  EXPECT_FALSE(root.get());
+  EXPECT_EQ("Line: 1, column: 3, Syntax error.", error.message);
+  root = ParseJSON("[\"\x07\"]", &error);
+  EXPECT_FALSE(root.get());
+  EXPECT_EQ("Line: 1, column: 4, Syntax error.", error.message);
+  root = ParseJSON("{\"\x09\": true}", &error);
+  EXPECT_FALSE(root.get());
+  EXPECT_EQ("Line: 1, column: 4, Syntax error.", error.message);
+  root = ParseJSON("{\"key\": \"\x01\"}", &error);
+  EXPECT_FALSE(root.get());
+  EXPECT_EQ("Line: 1, column: 11, Syntax error.", error.message);
 
   // Basic array
   root = ParseJSON("[true, false, null]");
@@ -410,6 +428,33 @@
   ASSERT_TRUE(root2.get());
   EXPECT_EQ(root->ToJSONString(), root2->ToJSONString());
 
+  // Test that allowed whitespace is limited to TAB, CR, LF and SP. There are
+  // several other Unicode characters defined as whitespace, so a selection of
+  // them are tested to ensure that they are not allowed.
+  // U+0009 CHARACTER TABULATION is allowed
+  root = ParseJSON("\t{\t\"key\"\t:\t[\t\"value1\"\t,\t\"value2\"\t]\t}\t");
+  ASSERT_TRUE(root.get());
+  // U+000A LINE FEED is allowed
+  root = ParseJSON("\n{\n\"key\"\n:\n[\n\"value1\"\n,\n\"value2\"\n]\n}\n");
+  ASSERT_TRUE(root.get());
+  // U+000D CARRIAGE RETURN is allowed
+  root = ParseJSON("\r{\r\"key\"\r:\r[\r\"value1\"\r,\r\"value2\"\r]\r}\r");
+  ASSERT_TRUE(root.get());
+  // U+0020 SPACE is allowed
+  root = ParseJSON(" { \"key\" : [ \"value1\" , \"value2\" ] } ");
+  ASSERT_TRUE(root.get());
+  // U+000B LINE TABULATION is not allowed
+  root = ParseJSON("[\x0b\"value\"]");
+  ASSERT_FALSE(root.get());
+  // U+00A0 NO-BREAK SPACE is not allowed
+  UChar invalid_space_1[] = {0x5b, 0x00a0, 0x5d};  // [<U+00A0>]
+  root = ParseJSON(String(invalid_space_1, base::size(invalid_space_1)));
+  ASSERT_FALSE(root.get());
+  // U+3000 IDEOGRAPHIC SPACE is not allowed
+  UChar invalid_space_2[] = {0x5b, 0x3000, 0x5d};  // [<U+3000>]
+  root = ParseJSON(String(invalid_space_2, base::size(invalid_space_2)));
+  ASSERT_FALSE(root.get());
+
   // Test nesting
   root = ParseJSON("{\"inner\":{\"array\":[true]},\"false\":false,\"d\":{}}");
   ASSERT_TRUE(root.get());
diff --git a/third_party/blink/renderer/platform/scheduler/main_thread/main_thread_metrics_helper.cc b/third_party/blink/renderer/platform/scheduler/main_thread/main_thread_metrics_helper.cc
index 5f9d438f..d264060 100644
--- a/third_party/blink/renderer/platform/scheduler/main_thread/main_thread_metrics_helper.cc
+++ b/third_party/blink/renderer/platform/scheduler/main_thread/main_thread_metrics_helper.cc
@@ -117,6 +117,10 @@
           DURATION_PER_TASK_TYPE_METRIC_NAME ".Foreground"),
       background_per_task_type_duration_reporter_(
           DURATION_PER_TASK_TYPE_METRIC_NAME ".Background"),
+      background_after_fifth_minute_per_task_type_duration_reporter_(
+          DURATION_PER_TASK_TYPE_METRIC_NAME ".Background.AfterFifthMinute"),
+      background_after_tenth_minute_per_task_type_duration_reporter_(
+          DURATION_PER_TASK_TYPE_METRIC_NAME ".Background.AfterTenthMinute"),
       per_task_use_case_duration_reporter_(DURATION_PER_TASK_USE_CASE_NAME),
       main_thread_task_load_state_(MainThreadTaskLoadState::kUnknown) {
   main_thread_load_tracker_.Resume(now);
@@ -331,6 +335,21 @@
     }
 
     background_per_task_type_duration_reporter_.RecordTask(task_type, duration);
+
+    background_after_fifth_minute_per_task_type_duration_reporter_.RecordTask(
+        task_type,
+        DurationOfIntervalOverlap(
+            start_time, end_time,
+            backgrounded_at + base::TimeDelta::FromMinutes(5),
+            std::max(backgrounded_at + base::TimeDelta::FromMinutes(5),
+                     end_time)));
+    background_after_tenth_minute_per_task_type_duration_reporter_.RecordTask(
+        task_type,
+        DurationOfIntervalOverlap(
+            start_time, end_time,
+            backgrounded_at + base::TimeDelta::FromMinutes(10),
+            std::max(backgrounded_at + base::TimeDelta::FromMinutes(10),
+                     end_time)));
   } else {
     per_queue_type_reporters_.foreground.RecordTask(queue_type, duration);
 
diff --git a/third_party/blink/renderer/platform/scheduler/main_thread/main_thread_metrics_helper.h b/third_party/blink/renderer/platform/scheduler/main_thread/main_thread_metrics_helper.h
index 101985e..d61b654 100644
--- a/third_party/blink/renderer/platform/scheduler/main_thread/main_thread_metrics_helper.h
+++ b/third_party/blink/renderer/platform/scheduler/main_thread/main_thread_metrics_helper.h
@@ -121,6 +121,10 @@
       foreground_per_task_type_duration_reporter_;
   TaskDurationPerTaskTypeMetricReporter
       background_per_task_type_duration_reporter_;
+  TaskDurationPerTaskTypeMetricReporter
+      background_after_fifth_minute_per_task_type_duration_reporter_;
+  TaskDurationPerTaskTypeMetricReporter
+      background_after_tenth_minute_per_task_type_duration_reporter_;
 
   TaskDurationMetricReporter<UseCase> per_task_use_case_duration_reporter_;
 
diff --git a/third_party/blink/renderer/platform/scroll/scroll_snap_data.h b/third_party/blink/renderer/platform/scroll/scroll_snap_data.h
index 0aa5294..6cc9550 100644
--- a/third_party/blink/renderer/platform/scroll/scroll_snap_data.h
+++ b/third_party/blink/renderer/platform/scroll/scroll_snap_data.h
@@ -6,7 +6,6 @@
 #define THIRD_PARTY_BLINK_RENDERER_PLATFORM_SCROLL_SCROLL_SNAP_DATA_H_
 
 #include "cc/input/scroll_snap_data.h"
-#include "cc/input/snap_fling_controller.h"
 
 // This file defines classes and structs used in SnapCoordinator.h
 
@@ -20,8 +19,6 @@
 using ScrollSnapAlign = cc::ScrollSnapAlign;
 using SnapAreaData = cc::SnapAreaData;
 using SnapContainerData = cc::SnapContainerData;
-using SnapFlingController = cc::SnapFlingController;
-using SnapFlingClient = cc::SnapFlingClient;
 
 }  // namespace blink
 
diff --git a/third_party/blink/tools/audit_non_blink_usage.py b/third_party/blink/tools/audit_non_blink_usage.py
index d11b59d..189c6b8 100755
--- a/third_party/blink/tools/audit_non_blink_usage.py
+++ b/third_party/blink/tools/audit_non_blink_usage.py
@@ -146,7 +146,6 @@
             'gfx::Size',
             'gfx::SizeF',
             'gfx::Transform',
-            'gfx::Vector2dF',
             # Wrapper of SkRegion used in Chromium.
             'cc::Region',
 
diff --git a/third_party/fuchsia-sdk/BUILD.gn b/third_party/fuchsia-sdk/BUILD.gn
index 00c4910..e14fd17 100644
--- a/third_party/fuchsia-sdk/BUILD.gn
+++ b/third_party/fuchsia-sdk/BUILD.gn
@@ -145,6 +145,17 @@
   ]
 }
 
+fuchsia_sdk_fidl_pkg("fonts") {
+  namespace = "fuchsia"
+  namespace_path = "fuchsia"
+  sources = [
+    "font_provider.fidl",
+  ]
+  deps = [
+    ":mem",
+  ]
+}
+
 fuchsia_sdk_fidl_pkg("gfx") {
   namespace = "fuchsia.ui"
   namespace_path = "fuchsia/ui"
diff --git a/third_party/inspector_protocol/README.chromium b/third_party/inspector_protocol/README.chromium
index ee2a9f1..b3a26a0 100644
--- a/third_party/inspector_protocol/README.chromium
+++ b/third_party/inspector_protocol/README.chromium
@@ -2,7 +2,7 @@
 Short Name: inspector_protocol
 URL: https://chromium.googlesource.com/deps/inspector_protocol/
 Version: 0
-Revision: 7efd53047e3df9993c0c1653bb674f2de9b93053
+Revision: 5e219fd8e92b250684f978bf0ee237e83d77c682
 License: BSD
 License File: LICENSE
 Security Critical: no
diff --git a/third_party/inspector_protocol/code_generator.py b/third_party/inspector_protocol/code_generator.py
index 7c5c57e2..38e7d1d 100644
--- a/third_party/inspector_protocol/code_generator.py
+++ b/third_party/inspector_protocol/code_generator.py
@@ -453,8 +453,7 @@
                 elif type["type"] == "object":
                     self.type_definitions[type_name] = create_user_type_definition(domain["domain"], type)
                 elif type["type"] == "array":
-                    items_type = type["items"]["type"]
-                    self.type_definitions[type_name] = wrap_array_definition(self.type_definitions[items_type])
+                    self.type_definitions[type_name] = self.resolve_type(type)
                 elif type["type"] == domain["domain"] + ".string":
                     self.type_definitions[type_name] = create_string_type_definition()
                 else:
diff --git a/third_party/libvpx/README.chromium b/third_party/libvpx/README.chromium
index 60a39ec0..122b1d3e 100644
--- a/third_party/libvpx/README.chromium
+++ b/third_party/libvpx/README.chromium
@@ -5,9 +5,9 @@
 License File: source/libvpx/LICENSE
 Security Critical: yes
 
-Date: Monday June 25 2018
+Date: Monday July 02 2018
 Branch: master
-Commit: 583859d7395ca70c3b1ca0acc1258a720470a807
+Commit: 03abd2c8f358549a445dce452e26aa72d3853e55
 
 Description:
 Contains the sources used to compile libvpx binaries used by Google Chrome and
diff --git a/third_party/libvpx/source/config/linux/ia32/vpx_dsp_rtcd.h b/third_party/libvpx/source/config/linux/ia32/vpx_dsp_rtcd.h
index ecd6c94b..391457a4 100644
--- a/third_party/libvpx/source/config/linux/ia32/vpx_dsp_rtcd.h
+++ b/third_party/libvpx/source/config/linux/ia32/vpx_dsp_rtcd.h
@@ -3636,10 +3636,12 @@
                                                      unsigned int* sse);
 
 unsigned int vpx_highbd_avg_4x4_c(const uint8_t*, int p);
-#define vpx_highbd_avg_4x4 vpx_highbd_avg_4x4_c
+unsigned int vpx_highbd_avg_4x4_sse2(const uint8_t*, int p);
+RTCD_EXTERN unsigned int (*vpx_highbd_avg_4x4)(const uint8_t*, int p);
 
 unsigned int vpx_highbd_avg_8x8_c(const uint8_t*, int p);
-#define vpx_highbd_avg_8x8 vpx_highbd_avg_8x8_c
+unsigned int vpx_highbd_avg_8x8_sse2(const uint8_t*, int p);
+RTCD_EXTERN unsigned int (*vpx_highbd_avg_8x8)(const uint8_t*, int p);
 
 void vpx_highbd_comp_avg_pred_c(uint16_t* comp_pred,
                                 const uint16_t* pred,
@@ -9461,6 +9463,12 @@
   vpx_highbd_8_variance8x8 = vpx_highbd_8_variance8x8_c;
   if (flags & HAS_SSE2)
     vpx_highbd_8_variance8x8 = vpx_highbd_8_variance8x8_sse2;
+  vpx_highbd_avg_4x4 = vpx_highbd_avg_4x4_c;
+  if (flags & HAS_SSE2)
+    vpx_highbd_avg_4x4 = vpx_highbd_avg_4x4_sse2;
+  vpx_highbd_avg_8x8 = vpx_highbd_avg_8x8_c;
+  if (flags & HAS_SSE2)
+    vpx_highbd_avg_8x8 = vpx_highbd_avg_8x8_sse2;
   vpx_highbd_convolve8 = vpx_highbd_convolve8_c;
   if (flags & HAS_AVX2)
     vpx_highbd_convolve8 = vpx_highbd_convolve8_avx2;
diff --git a/third_party/libvpx/source/config/linux/x64/vpx_dsp_rtcd.h b/third_party/libvpx/source/config/linux/x64/vpx_dsp_rtcd.h
index 258994f..bfc0d8c3 100644
--- a/third_party/libvpx/source/config/linux/x64/vpx_dsp_rtcd.h
+++ b/third_party/libvpx/source/config/linux/x64/vpx_dsp_rtcd.h
@@ -2949,10 +2949,12 @@
 #define vpx_highbd_8_variance8x8 vpx_highbd_8_variance8x8_sse2
 
 unsigned int vpx_highbd_avg_4x4_c(const uint8_t*, int p);
-#define vpx_highbd_avg_4x4 vpx_highbd_avg_4x4_c
+unsigned int vpx_highbd_avg_4x4_sse2(const uint8_t*, int p);
+#define vpx_highbd_avg_4x4 vpx_highbd_avg_4x4_sse2
 
 unsigned int vpx_highbd_avg_8x8_c(const uint8_t*, int p);
-#define vpx_highbd_avg_8x8 vpx_highbd_avg_8x8_c
+unsigned int vpx_highbd_avg_8x8_sse2(const uint8_t*, int p);
+#define vpx_highbd_avg_8x8 vpx_highbd_avg_8x8_sse2
 
 void vpx_highbd_comp_avg_pred_c(uint16_t* comp_pred,
                                 const uint16_t* pred,
diff --git a/third_party/libvpx/source/config/mac/ia32/vpx_dsp_rtcd.h b/third_party/libvpx/source/config/mac/ia32/vpx_dsp_rtcd.h
index ecd6c94b..391457a4 100644
--- a/third_party/libvpx/source/config/mac/ia32/vpx_dsp_rtcd.h
+++ b/third_party/libvpx/source/config/mac/ia32/vpx_dsp_rtcd.h
@@ -3636,10 +3636,12 @@
                                                      unsigned int* sse);
 
 unsigned int vpx_highbd_avg_4x4_c(const uint8_t*, int p);
-#define vpx_highbd_avg_4x4 vpx_highbd_avg_4x4_c
+unsigned int vpx_highbd_avg_4x4_sse2(const uint8_t*, int p);
+RTCD_EXTERN unsigned int (*vpx_highbd_avg_4x4)(const uint8_t*, int p);
 
 unsigned int vpx_highbd_avg_8x8_c(const uint8_t*, int p);
-#define vpx_highbd_avg_8x8 vpx_highbd_avg_8x8_c
+unsigned int vpx_highbd_avg_8x8_sse2(const uint8_t*, int p);
+RTCD_EXTERN unsigned int (*vpx_highbd_avg_8x8)(const uint8_t*, int p);
 
 void vpx_highbd_comp_avg_pred_c(uint16_t* comp_pred,
                                 const uint16_t* pred,
@@ -9461,6 +9463,12 @@
   vpx_highbd_8_variance8x8 = vpx_highbd_8_variance8x8_c;
   if (flags & HAS_SSE2)
     vpx_highbd_8_variance8x8 = vpx_highbd_8_variance8x8_sse2;
+  vpx_highbd_avg_4x4 = vpx_highbd_avg_4x4_c;
+  if (flags & HAS_SSE2)
+    vpx_highbd_avg_4x4 = vpx_highbd_avg_4x4_sse2;
+  vpx_highbd_avg_8x8 = vpx_highbd_avg_8x8_c;
+  if (flags & HAS_SSE2)
+    vpx_highbd_avg_8x8 = vpx_highbd_avg_8x8_sse2;
   vpx_highbd_convolve8 = vpx_highbd_convolve8_c;
   if (flags & HAS_AVX2)
     vpx_highbd_convolve8 = vpx_highbd_convolve8_avx2;
diff --git a/third_party/libvpx/source/config/mac/x64/vpx_dsp_rtcd.h b/third_party/libvpx/source/config/mac/x64/vpx_dsp_rtcd.h
index 258994f..bfc0d8c3 100644
--- a/third_party/libvpx/source/config/mac/x64/vpx_dsp_rtcd.h
+++ b/third_party/libvpx/source/config/mac/x64/vpx_dsp_rtcd.h
@@ -2949,10 +2949,12 @@
 #define vpx_highbd_8_variance8x8 vpx_highbd_8_variance8x8_sse2
 
 unsigned int vpx_highbd_avg_4x4_c(const uint8_t*, int p);
-#define vpx_highbd_avg_4x4 vpx_highbd_avg_4x4_c
+unsigned int vpx_highbd_avg_4x4_sse2(const uint8_t*, int p);
+#define vpx_highbd_avg_4x4 vpx_highbd_avg_4x4_sse2
 
 unsigned int vpx_highbd_avg_8x8_c(const uint8_t*, int p);
-#define vpx_highbd_avg_8x8 vpx_highbd_avg_8x8_c
+unsigned int vpx_highbd_avg_8x8_sse2(const uint8_t*, int p);
+#define vpx_highbd_avg_8x8 vpx_highbd_avg_8x8_sse2
 
 void vpx_highbd_comp_avg_pred_c(uint16_t* comp_pred,
                                 const uint16_t* pred,
diff --git a/third_party/libvpx/source/config/vpx_version.h b/third_party/libvpx/source/config/vpx_version.h
index 477054b..a7a120f 100644
--- a/third_party/libvpx/source/config/vpx_version.h
+++ b/third_party/libvpx/source/config/vpx_version.h
@@ -2,7 +2,7 @@
 #define VERSION_MAJOR  1
 #define VERSION_MINOR  7
 #define VERSION_PATCH  0
-#define VERSION_EXTRA  "538-g583859d73"
+#define VERSION_EXTRA  "589-g03abd2c8f"
 #define VERSION_PACKED ((VERSION_MAJOR<<16)|(VERSION_MINOR<<8)|(VERSION_PATCH))
-#define VERSION_STRING_NOSP "v1.7.0-538-g583859d73"
-#define VERSION_STRING      " v1.7.0-538-g583859d73"
+#define VERSION_STRING_NOSP "v1.7.0-589-g03abd2c8f"
+#define VERSION_STRING      " v1.7.0-589-g03abd2c8f"
diff --git a/third_party/libvpx/source/config/win/ia32/vpx_dsp_rtcd.h b/third_party/libvpx/source/config/win/ia32/vpx_dsp_rtcd.h
index ecd6c94b..391457a4 100644
--- a/third_party/libvpx/source/config/win/ia32/vpx_dsp_rtcd.h
+++ b/third_party/libvpx/source/config/win/ia32/vpx_dsp_rtcd.h
@@ -3636,10 +3636,12 @@
                                                      unsigned int* sse);
 
 unsigned int vpx_highbd_avg_4x4_c(const uint8_t*, int p);
-#define vpx_highbd_avg_4x4 vpx_highbd_avg_4x4_c
+unsigned int vpx_highbd_avg_4x4_sse2(const uint8_t*, int p);
+RTCD_EXTERN unsigned int (*vpx_highbd_avg_4x4)(const uint8_t*, int p);
 
 unsigned int vpx_highbd_avg_8x8_c(const uint8_t*, int p);
-#define vpx_highbd_avg_8x8 vpx_highbd_avg_8x8_c
+unsigned int vpx_highbd_avg_8x8_sse2(const uint8_t*, int p);
+RTCD_EXTERN unsigned int (*vpx_highbd_avg_8x8)(const uint8_t*, int p);
 
 void vpx_highbd_comp_avg_pred_c(uint16_t* comp_pred,
                                 const uint16_t* pred,
@@ -9461,6 +9463,12 @@
   vpx_highbd_8_variance8x8 = vpx_highbd_8_variance8x8_c;
   if (flags & HAS_SSE2)
     vpx_highbd_8_variance8x8 = vpx_highbd_8_variance8x8_sse2;
+  vpx_highbd_avg_4x4 = vpx_highbd_avg_4x4_c;
+  if (flags & HAS_SSE2)
+    vpx_highbd_avg_4x4 = vpx_highbd_avg_4x4_sse2;
+  vpx_highbd_avg_8x8 = vpx_highbd_avg_8x8_c;
+  if (flags & HAS_SSE2)
+    vpx_highbd_avg_8x8 = vpx_highbd_avg_8x8_sse2;
   vpx_highbd_convolve8 = vpx_highbd_convolve8_c;
   if (flags & HAS_AVX2)
     vpx_highbd_convolve8 = vpx_highbd_convolve8_avx2;
diff --git a/third_party/libvpx/source/config/win/x64/vpx_dsp_rtcd.h b/third_party/libvpx/source/config/win/x64/vpx_dsp_rtcd.h
index 258994f..bfc0d8c3 100644
--- a/third_party/libvpx/source/config/win/x64/vpx_dsp_rtcd.h
+++ b/third_party/libvpx/source/config/win/x64/vpx_dsp_rtcd.h
@@ -2949,10 +2949,12 @@
 #define vpx_highbd_8_variance8x8 vpx_highbd_8_variance8x8_sse2
 
 unsigned int vpx_highbd_avg_4x4_c(const uint8_t*, int p);
-#define vpx_highbd_avg_4x4 vpx_highbd_avg_4x4_c
+unsigned int vpx_highbd_avg_4x4_sse2(const uint8_t*, int p);
+#define vpx_highbd_avg_4x4 vpx_highbd_avg_4x4_sse2
 
 unsigned int vpx_highbd_avg_8x8_c(const uint8_t*, int p);
-#define vpx_highbd_avg_8x8 vpx_highbd_avg_8x8_c
+unsigned int vpx_highbd_avg_8x8_sse2(const uint8_t*, int p);
+#define vpx_highbd_avg_8x8 vpx_highbd_avg_8x8_sse2
 
 void vpx_highbd_comp_avg_pred_c(uint16_t* comp_pred,
                                 const uint16_t* pred,
diff --git a/third_party/robolectric/README.chromium b/third_party/robolectric/README.chromium
index 44751bb..03d4238 100644
--- a/third_party/robolectric/README.chromium
+++ b/third_party/robolectric/README.chromium
@@ -13,6 +13,8 @@
   with GN.
 - Provided stub implementation of MavenDependencyResolver.java to avoid
   needing maven third_party libraries.
+- Added custom_asynctask folder to have workable copies of shadows required
+  for our own implementation of AsyncTask
 How To Update:
 - Visit the migration guide to see which APIs changed and need updating.
   http://robolectric.org/migrating/
diff --git a/third_party/robolectric/custom_asynctask/java/src/org/chromium/base/test/asynctask/ShadowAsyncTask.java b/third_party/robolectric/custom_asynctask/java/src/org/chromium/base/test/asynctask/ShadowAsyncTask.java
new file mode 100644
index 0000000..93bfb2c
--- /dev/null
+++ b/third_party/robolectric/custom_asynctask/java/src/org/chromium/base/test/asynctask/ShadowAsyncTask.java
@@ -0,0 +1,163 @@
+package org.chromium.base.test.asynctask;
+
+import org.chromium.base.AsyncTask;
+import java.util.concurrent.Callable;
+import java.util.concurrent.CancellationException;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.Executor;
+import java.util.concurrent.FutureTask;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.TimeoutException;
+import org.robolectric.annotation.Implementation;
+import org.robolectric.annotation.Implements;
+import org.robolectric.annotation.RealObject;
+import org.robolectric.shadows.ShadowApplication;
+
+@Implements(AsyncTask.class)
+public class ShadowAsyncTask<Params, Progress, Result> {
+    @RealObject
+    private AsyncTask<Params, Progress, Result> realAsyncTask;
+
+    private final FutureTask<Result> future;
+    private final BackgroundWorker worker;
+    private AsyncTask.Status status = AsyncTask.Status.PENDING;
+
+    public ShadowAsyncTask() {
+        worker = new BackgroundWorker();
+        future = new FutureTask<Result>(worker) {
+            @Override
+            protected void done() {
+                status = AsyncTask.Status.FINISHED;
+                try {
+                    final Result result = get();
+
+                    try {
+                        ShadowApplication.getInstance().getForegroundThreadScheduler().post(
+                                new Runnable() {
+                                    @Override
+                                    public void run() {
+                                        getBridge().onPostExecute(result);
+                                    }
+                                });
+                    } catch (Throwable t) {
+                        throw new OnPostExecuteException(t);
+                    }
+                } catch (CancellationException e) {
+                    ShadowApplication.getInstance().getForegroundThreadScheduler().post(
+                            new Runnable() {
+                                @Override
+                                public void run() {
+                                    getBridge().onCancelled();
+                                }
+                            });
+                } catch (InterruptedException e) {
+                    // Ignore.
+                } catch (OnPostExecuteException e) {
+                    throw new RuntimeException(e.getCause());
+                } catch (Throwable t) {
+                    throw new RuntimeException(
+                            "An error occured while executing doInBackground()", t.getCause());
+                }
+            }
+        };
+    }
+
+    @Implementation
+    public boolean isCancelled() {
+        return future.isCancelled();
+    }
+
+    @Implementation
+    public boolean cancel(boolean mayInterruptIfRunning) {
+        return future.cancel(mayInterruptIfRunning);
+    }
+
+    @Implementation
+    public Result get() throws InterruptedException, ExecutionException {
+        return future.get();
+    }
+
+    @Implementation
+    public Result get(long timeout, TimeUnit unit)
+            throws InterruptedException, ExecutionException, TimeoutException {
+        return future.get(timeout, unit);
+    }
+
+    @SuppressWarnings("unchecked")
+    @Implementation
+    public AsyncTask<Params, Progress, Result> execute(final Params... params) {
+        status = AsyncTask.Status.RUNNING;
+        getBridge().onPreExecute();
+
+        worker.params = params;
+
+        ShadowApplication.getInstance().getBackgroundThreadScheduler().post(new Runnable() {
+            @Override
+            public void run() {
+                future.run();
+            }
+        });
+
+        return realAsyncTask;
+    }
+
+    @SuppressWarnings("unchecked")
+    @Implementation
+    public AsyncTask<Params, Progress, Result> executeOnExecutor(
+            Executor executor, Params... params) {
+        status = AsyncTask.Status.RUNNING;
+        getBridge().onPreExecute();
+
+        worker.params = params;
+        executor.execute(new Runnable() {
+            @Override
+            public void run() {
+                future.run();
+            }
+        });
+
+        return realAsyncTask;
+    }
+
+    @Implementation
+    public AsyncTask.Status getStatus() {
+        return status;
+    }
+
+    /**
+     * Enqueue a call to {@link AsyncTask#onProgressUpdate(Object[])} on UI looper (or run it
+     * immediately if the looper it is not paused).
+     *
+     * @param values The progress values to update the UI with.
+     * @see AsyncTask#publishProgress(Object[])
+     */
+    @SuppressWarnings("unchecked")
+    @Implementation
+    public void publishProgress(final Progress... values) {
+        ShadowApplication.getInstance().getForegroundThreadScheduler().post(new Runnable() {
+            @Override
+            public void run() {
+                getBridge().onProgressUpdate(values);
+            }
+        });
+    }
+
+    private ShadowAsyncTaskBridge<Params, Progress, Result> getBridge() {
+        return new ShadowAsyncTaskBridge<>(realAsyncTask);
+    }
+
+    private final class BackgroundWorker implements Callable<Result> {
+        Params[] params;
+
+        @Override
+        public Result call() throws Exception {
+            return getBridge().doInBackground(params);
+        }
+    }
+
+    private static class OnPostExecuteException extends Exception {
+        public OnPostExecuteException(Throwable throwable) {
+            super(throwable);
+        }
+    }
+}
diff --git a/third_party/robolectric/custom_asynctask/java/src/org/chromium/base/test/asynctask/ShadowAsyncTaskBridge.java b/third_party/robolectric/custom_asynctask/java/src/org/chromium/base/test/asynctask/ShadowAsyncTaskBridge.java
new file mode 100644
index 0000000..2082aec
--- /dev/null
+++ b/third_party/robolectric/custom_asynctask/java/src/org/chromium/base/test/asynctask/ShadowAsyncTaskBridge.java
@@ -0,0 +1,43 @@
+package org.chromium.base.test.asynctask;
+
+import org.chromium.base.AsyncTask;
+import org.robolectric.annotation.internal.DoNotInstrument;
+import org.robolectric.util.ReflectionHelpers;
+import org.robolectric.util.ReflectionHelpers.ClassParameter;
+
+/**
+ * Bridge between shadows and {@link org.chromium.base.AsyncTask}.
+ */
+@DoNotInstrument
+public class ShadowAsyncTaskBridge<Params, Progress, Result> {
+    private AsyncTask<Params, Progress, Result> asyncTask;
+
+    public ShadowAsyncTaskBridge(AsyncTask<Params, Progress, Result> asyncTask) {
+        this.asyncTask = asyncTask;
+    }
+
+    @SuppressWarnings("unchecked")
+    public Result doInBackground(Params... params) {
+        return ReflectionHelpers.callInstanceMethod(
+                asyncTask, "doInBackground", ClassParameter.from(Object[].class, params));
+    }
+
+    public void onPreExecute() {
+        ReflectionHelpers.callInstanceMethod(asyncTask, "onPreExecute");
+    }
+
+    public void onPostExecute(Result result) {
+        ReflectionHelpers.callInstanceMethod(
+                asyncTask, "onPostExecute", ClassParameter.from(Object.class, result));
+    }
+
+    @SuppressWarnings("unchecked")
+    public void onProgressUpdate(Progress... values) {
+        ReflectionHelpers.callInstanceMethod(
+                asyncTask, "onProgressUpdate", ClassParameter.from(Object[].class, values));
+    }
+
+    public void onCancelled() {
+        ReflectionHelpers.callInstanceMethod(asyncTask, "onCancelled");
+    }
+}
diff --git a/tools/metrics/histograms/enums.xml b/tools/metrics/histograms/enums.xml
index ad62223d..987e23c 100644
--- a/tools/metrics/histograms/enums.xml
+++ b/tools/metrics/histograms/enums.xml
@@ -18156,7 +18156,7 @@
   <int value="1417" label="AddEventListenerPassiveTrue"/>
   <int value="1418" label="AddEventListenerPassiveFalse"/>
   <int value="1419" label="CSPReferrerDirective"/>
-  <int value="1420" label="DocumentOpen"/>
+  <int value="1420" label="OBSOLETE_DocumentOpen"/>
   <int value="1421" label="ElementRequestPointerLockInShadow"/>
   <int value="1422" label="ShadowRootPointerLockElement"/>
   <int value="1423" label="DocumentPointerLockElementInV0Shadow"/>
@@ -19269,6 +19269,9 @@
   <int value="2491" label="CSSEnvironmentVariable_SafeAreaInsetBottom"/>
   <int value="2492" label="CSSEnvironmentVariable_SafeAreaInsetRight"/>
   <int value="2493" label="MediaControlsDisplayCutoutGesture"/>
+  <int value="2494" label="DocumentOpenTwoArgs"/>
+  <int value="2495" label="DocumentOpenTwoArgsWithReplace"/>
+  <int value="2496" label="DocumentOpenThreeArgs"/>
 </enum>
 
 <enum name="FeedbackSource">
@@ -31243,6 +31246,30 @@
   </int>
 </enum>
 
+<enum name="MountEncryptedEncryptionKeyStatus">
+  <int value="0" label="unknown">Key not loaded yet.</int>
+  <int value="1" label="keyfile">Key loaded from encrypted.key file.</int>
+  <int value="2" label="NeedsFinalization">
+    Key loaded from needs-finalization file.
+  </int>
+  <int value="3" label="Fresh">Freshly generated key.</int>
+</enum>
+
+<enum name="MountEncryptedSystemKeyStatus">
+  <int value="0" label="Unknown">No key loaded yet.</int>
+  <int value="1" label="NVRAMLockbox">Using lockbox salt as system key.</int>
+  <int value="2" label="NVRAMEncstateful">
+    Key in dedicated encstateful NVRAM space.
+  </int>
+  <int value="3" label="FinalizationPending">
+    TPM not ready, obfuscated key on disk.
+  </int>
+  <int value="4" label="Factory">Hard-coded factory key.</int>
+  <int value="5" label="KernelCommandLine">Key from kernel command line.</int>
+  <int value="6" label="ProductUUID">Using product UUID as system key.</int>
+  <int value="7" label="StaticFallback">Using hard-coded fallback key.</int>
+</enum>
+
 <enum name="MouseEventFollowedByClick">
   <int value="0" label="Missed event before click"/>
   <int value="1" label="Caught event before click"/>
diff --git a/tools/metrics/histograms/histograms.xml b/tools/metrics/histograms/histograms.xml
index e88e9a1b..63697cf 100644
--- a/tools/metrics/histograms/histograms.xml
+++ b/tools/metrics/histograms/histograms.xml
@@ -587,6 +587,10 @@
 
 <histogram name="AnchorElementMetrics.Clicked.HrefEngagementScore"
     units="score">
+  <obsolete>
+    Deprecated 07/2018. Replaced with
+    AnchorElementMetrics.Clicked.HrefEngagementScore2.
+  </obsolete>
   <owner>chelu@chromium.org</owner>
   <summary>
     The target link (href) engagement score of an anchor element. The score is
@@ -596,6 +600,16 @@
   </summary>
 </histogram>
 
+<histogram name="AnchorElementMetrics.Clicked.HrefEngagementScore2"
+    units="score">
+  <owner>chelu@chromium.org</owner>
+  <summary>
+    The site engagement score of the target link (href) of an anchor element.
+    The score is retrived from the site engagement service. This histogram is
+    recorded when the anchor element is clicked.
+  </summary>
+</histogram>
+
 <histogram name="AnchorElementMetrics.Clicked.IsInIFrame" enum="Boolean">
   <owner>chelu@chromium.org</owner>
   <summary>
@@ -3417,161 +3431,45 @@
   </summary>
 </histogram>
 
-<histogram name="Arc.Provisioning.Result" enum="ArcProvisioningResult">
-  <obsolete>
-    Deprecated on 2016-09-15 and replaced by Arc.Provisioning.Result.Managed and
-    Arc.Provisioning.Result.Unmanaged.
-  </obsolete>
-  <owner>alexchau@google.com</owner>
-  <owner>phweiss@google.com</owner>
-  <summary>
-    The result (success or the type of failure) of ARC provisioning.
-  </summary>
-</histogram>
-
-<histogram name="Arc.Provisioning.Result.Child" enum="ArcProvisioningResult">
-  <owner>alexchau@google.com</owner>
-  <owner>phweiss@google.com</owner>
-  <summary>
-    The result (success or the type of failure) of ARC provisioning for child
-    accounts.
-  </summary>
-</histogram>
-
-<histogram name="Arc.Provisioning.Result.Managed" enum="ArcProvisioningResult">
-  <owner>alexchau@google.com</owner>
-  <owner>phweiss@google.com</owner>
-  <summary>
-    The result (success or the type of failure) of ARC provisioning on managed
-    devices.
-  </summary>
-</histogram>
-
-<histogram name="Arc.Provisioning.Result.RobotAccount"
+<histogram base="true" name="Arc.Provisioning.Result"
     enum="ArcProvisioningResult">
+<!-- Name completed by histogram_suffixes name="ArcUserTypes" -->
+
   <owner>alexchau@google.com</owner>
   <owner>phweiss@google.com</owner>
   <summary>
-    The result (success or the type of failure) of ARC provisioning on managed
-    devices with a robot account (Public Session or Kiosk).
+    The result (success or the type of failure) of ARC provisioning. The base
+    value has been deprecated on 2016-09-15 in favour of per user type values.
   </summary>
 </histogram>
 
-<histogram name="Arc.Provisioning.Result.Unmanaged"
-    enum="ArcProvisioningResult">
+<histogram name="Arc.Provisioning.TimeDelta.Failure" units="ms">
+<!-- Name completed by histogram_suffixes name="ArcUserTypes" -->
+
   <owner>alexchau@google.com</owner>
   <owner>phweiss@google.com</owner>
   <summary>
-    The result (success or the type of failure) of ARC provisioning on unmanaged
-    devices.
+    Elapsed time from the signing in process start to call to onSignInFailed.
   </summary>
 </histogram>
 
-<histogram name="Arc.Provisioning.TimeDelta.Failure.Child" units="ms">
-  <owner>alexchau@google.com</owner>
-  <owner>phweiss@google.com</owner>
-  <summary>
-    Elapsed time from the signing in process start to call to onSignInFailed for
-    child account.
-  </summary>
-</histogram>
+<histogram name="Arc.Provisioning.TimeDelta.Success" units="ms">
+<!-- Name completed by histogram_suffixes name="ArcUserTypes" -->
 
-<histogram name="Arc.Provisioning.TimeDelta.Failure.Managed" units="ms">
-  <owner>alexchau@google.com</owner>
-  <owner>phweiss@google.com</owner>
-  <summary>
-    Elapsed time from the signing in process start to call to onSignInFailed for
-    managed users.
-  </summary>
-</histogram>
-
-<histogram name="Arc.Provisioning.TimeDelta.Failure.RobotAccount" units="ms">
-  <owner>alexchau@google.com</owner>
-  <owner>phweiss@google.com</owner>
-  <summary>
-    Elapsed time from the signing in process start to call to onSignInFailed for
-    managed users with a robot account (Public Session or Kiosk).
-  </summary>
-</histogram>
-
-<histogram name="Arc.Provisioning.TimeDelta.Failure.Unmanaged" units="ms">
-  <owner>alexchau@google.com</owner>
-  <owner>phweiss@google.com</owner>
-  <summary>
-    Elapsed time from the signing in process start to call to onSignInFailed for
-    unmanaged users.
-  </summary>
-</histogram>
-
-<histogram name="Arc.Provisioning.TimeDelta.Success.Child" units="ms">
   <owner>alexchau@google.com</owner>
   <owner>phweiss@google.com</owner>
   <summary>
     Elapsed time from the signing in process start to successful call to
-    onSignInComplete for child account.
+    onSignInComplete.
   </summary>
 </histogram>
 
-<histogram name="Arc.Provisioning.TimeDelta.Success.Managed" units="ms">
-  <owner>alexchau@google.com</owner>
-  <owner>phweiss@google.com</owner>
-  <summary>
-    Elapsed time from the signing in process start to successful call to
-    onSignInComplete for managed users.
-  </summary>
-</histogram>
+<histogram name="Arc.Reauthorization.Result" enum="ArcProvisioningResult">
+<!-- Name completed by histogram_suffixes name="ArcUserTypes" -->
 
-<histogram name="Arc.Provisioning.TimeDelta.Success.RobotAccount" units="ms">
-  <owner>alexchau@google.com</owner>
-  <owner>phweiss@google.com</owner>
-  <summary>
-    Elapsed time from the signing in process start to successful call to
-    onSignInComplete for managed users with a robot account (Public Session or
-    Kiosk).
-  </summary>
-</histogram>
-
-<histogram name="Arc.Provisioning.TimeDelta.Success.Unmanaged" units="ms">
-  <owner>alexchau@google.com</owner>
-  <owner>phweiss@google.com</owner>
-  <summary>
-    Elapsed time from the signing in process start to successful call to
-    onSignInComplete for unmanaged users.
-  </summary>
-</histogram>
-
-<histogram name="Arc.Reauthorization.Result.Child" enum="ArcProvisioningResult">
   <owner>khmel@google.com</owner>
   <summary>
-    The result (success or the type of failure) of ARC reauthorization for child
-    account.
-  </summary>
-</histogram>
-
-<histogram name="Arc.Reauthorization.Result.Managed"
-    enum="ArcProvisioningResult">
-  <owner>khmel@google.com</owner>
-  <summary>
-    The result (success or the type of failure) of ARC reauthorization on
-    managed devices.
-  </summary>
-</histogram>
-
-<histogram name="Arc.Reauthorization.Result.RobotAccount"
-    enum="ArcProvisioningResult">
-  <owner>khmel@google.com</owner>
-  <summary>
-    The result (success or the type of failure) of ARC reauthorization on
-    managed devices with a robot account (Public Session or Kiosk).
-  </summary>
-</histogram>
-
-<histogram name="Arc.Reauthorization.Result.Unmanaged"
-    enum="ArcProvisioningResult">
-  <owner>khmel@google.com</owner>
-  <summary>
-    The result (success or the type of failure) of ARC reauthorization on
-    unmanaged devices.
+    The result (success or the type of failure) of ARC reauthorization.
   </summary>
 </histogram>
 
@@ -13509,6 +13407,18 @@
   </summary>
 </histogram>
 
+<histogram name="ContentSettings.Popups.StrongBlockerActivationPosition"
+    enum="SafeBrowsingActivationPosition">
+  <owner>csharrison@chromium.org</owner>
+  <owner>ericrobinson@chromium.org</owner>
+  <summary>
+    For pages that trigger Safe Browsing triggered popup blocker (in warn or
+    enforce modes), records the position in the redirect chain for the page the
+    activation was triggered by.  If SubresourceFilterConsiderRedirects is
+    disabled, then always returns &quot;Only navigation&quot;.
+  </summary>
+</histogram>
+
 <histogram name="ContentSuggestions.Feed.NetworkRequestStatusCode"
     enum="CombinedHttpResponseAndNetErrorCode">
   <owner>pnoland@chromium.org</owner>
@@ -15012,6 +14922,39 @@
   </summary>
 </histogram>
 
+<histogram name="CustomTabs.DynamicModule.CreateActivityDelegateTime"
+    units="ms">
+  <owner>mvanouwerkerk@chromium.org</owner>
+  <summary>
+    Time to create an activity delegate for a custom tabs dynamic module.
+    Android only.
+  </summary>
+</histogram>
+
+<histogram name="CustomTabs.DynamicModule.CreatePackageContextTime" units="ms">
+  <owner>mvanouwerkerk@chromium.org</owner>
+  <summary>
+    Time to create the package context for a custom tabs dynamic module. Android
+    only.
+  </summary>
+</histogram>
+
+<histogram name="CustomTabs.DynamicModule.EntryPointLoadClassTime" units="ms">
+  <owner>mvanouwerkerk@chromium.org</owner>
+  <summary>
+    Time to load the entry point class for a custom tabs dynamic module. Android
+    only.
+  </summary>
+</histogram>
+
+<histogram name="CustomTabs.DynamicModule.EntryPointNewInstanceTime" units="ms">
+  <owner>mvanouwerkerk@chromium.org</owner>
+  <summary>
+    Time to instantiate the entry point class for a custom tabs dynamic module.
+    Android only.
+  </summary>
+</histogram>
+
 <histogram name="CustomTabs.IntentToFirstCommitNavigationTime" units="ms">
   <obsolete>
     Deprecated 10/2016 in favor of .IntentToFirstCommitNavigationTime2.*.
@@ -18110,6 +18053,9 @@
 </histogram>
 
 <histogram name="DNS.AttemptCancelled">
+  <obsolete>
+    Deprecated as of 6/2018.
+  </obsolete>
   <owner>mgersh@chromium.org</owner>
   <summary>
     The attempt which completed after the job was already cancelled.
@@ -18172,6 +18118,9 @@
 </histogram>
 
 <histogram name="DNS.AttemptTimeSavedByRetry" units="ms">
+  <obsolete>
+    Deprecated as of 6/2018.
+  </obsolete>
   <owner>mgersh@chromium.org</owner>
   <summary>
     This histogram shows the time saved by having spawned an extra attempt, when
@@ -71150,6 +71099,27 @@
   </summary>
 </histogram>
 
+<histogram name="Platform.MountEncrypted.EncryptionKeyStatus"
+    enum="MountEncryptedEncryptionKeyStatus">
+  <owner>apronin@chromium.org</owner>
+  <owner>mnissler@chromium.org</owner>
+  <summary>
+    File system encryption key status for the encrypted stateful file system on
+    Chrome OS. The encryption key is the one that is used by the kernel to
+    protect actual file contents on disk.
+  </summary>
+</histogram>
+
+<histogram name="Platform.MountEncrypted.SystemKeyStatus"
+    enum="MountEncryptedSystemKeyStatus">
+  <owner>apronin@chromium.org</owner>
+  <owner>mnissler@chromium.org</owner>
+  <summary>
+    Type/Origin of the system key used for the encrypted stateful file system on
+    Chrome OS. This key is used to wrap the actual file system encryption key.
+  </summary>
+</histogram>
+
 <histogram name="Platform.PageFaultsLong" units="page faults/second">
   <owner>sonnyrao@chromium.org</owner>
   <summary>
@@ -78901,6 +78871,10 @@
 </histogram>
 
 <histogram name="Renderer4.CompositorThreadImplDrawDelay" units="ms">
+  <obsolete>
+    Deprecated in 06/2018, M69. Renderer4.CompositorThreadImplDrawDelay is no
+    longer needed due to issue 851784.
+  </obsolete>
   <owner>wiltzius@chromium.org</owner>
   <summary>
     Time between frames, as measured on the compositor thread. This is collected
@@ -79178,6 +79152,10 @@
 </histogram>
 
 <histogram name="Renderer4.LanguageDetection" units="ms">
+  <obsolete>
+    Deprecated in 06/2018, M69. Renderer4.LanguageDetection is no longer needed
+    due to issue 851784.
+  </obsolete>
   <owner>wiltzius@chromium.org</owner>
   <summary>
     Time to determine the page language. This is done after the page has been
@@ -79363,6 +79341,10 @@
 </histogram>
 
 <histogram name="Renderer4.SoftwareCompositorThreadImplDrawDelay" units="ms">
+  <obsolete>
+    Deprecated in 06/2018, M69. Renderer4.SoftwareCompositorThreadImplDrawDelay
+    is no longer needed due to issue 851784.
+  </obsolete>
   <owner>wiltzius@chromium.org</owner>
   <summary>
     Time between frames when the software renderer is being used, as measured on
@@ -114014,11 +113996,17 @@
 
 <histogram_suffixes name="ArcUserTypes" separator=".">
   <suffix name="Child" label="User with child accounts."/>
+  <suffix name="DemoMode" label="Demo devices with a demo mode robot account."/>
   <suffix name="Managed" label="User with forced Play Store feature"/>
+  <suffix name="OfflineDemoMode" label="Offline enrolled demo mode devices."/>
   <suffix name="RobotAccount"
       label="Managed devices with a robot account (Public Session or Kiosk)"/>
   <suffix name="Unmanaged" label="User with optional Play Store feature"/>
   <affected-histogram name="Arc.PlayStoreShown.TimeDelta"/>
+  <affected-histogram name="Arc.Provisioning.Result"/>
+  <affected-histogram name="Arc.Provisioning.TimeDelta.Failure"/>
+  <affected-histogram name="Arc.Provisioning.TimeDelta.Success"/>
+  <affected-histogram name="Arc.Reauthorization.Result"/>
 </histogram_suffixes>
 
 <histogram_suffixes name="AsyncDNSPref" separator="_">
@@ -123090,6 +123078,19 @@
   <affected-histogram name="Memory.Experimental.Renderer.V8MainThreadIsolate"/>
 </histogram_suffixes>
 
+<histogram_suffixes name="RendererScheduler.AfterNthMinuteInBackgroundSplit"
+    separator=".">
+  <suffix name="Background.AfterFifthMinute"
+      label="Time spent in tasks of a particular task type starting from the
+             sixth minute after backgrounding the renderer. The renderer is
+             expected to be mostly idle during this period."/>
+  <suffix name="Background.AfterTenthMinute"
+      label="Time spent in tasks of a particular task type starting from the
+             eleventh minute after backgrounding the renderer. The renderer
+             is expected to be mostly idle during this period."/>
+  <affected-histogram name="RendererScheduler.TaskDurationPerTaskType2"/>
+</histogram_suffixes>
+
 <histogram_suffixes name="RendererScheduler.ExpectedQueueingTime.FrameSplit"
     separator=".">
   <suffix name="CrossOriginBackground"
@@ -123203,65 +123204,65 @@
 <histogram_suffixes name="RendererScheduler.TaskDurationPerQueueTypeSplit"
     separator=".">
   <suffix name="Background"
-      label="Time spend in tasks of a particular type when the renderer is in
-             the background."/>
+      label="Time spent in tasks of a particular queue type when the renderer
+             is in the background."/>
   <suffix name="Background.AfterFifthMinute"
-      label="Time spent in tasks of a particular type starting from the sixth
-             minute after backgrounding the renderer. The renderer is
+      label="Time spent in tasks of a particular queue type starting from the
+             sixth minute after backgrounding the renderer. The renderer is
              expected to be mostly idle during this period."/>
   <suffix name="Background.AfterTenthMinute"
       label="Time spent in tasks of a particular type starting from the
              eleventh minute after backgrounding the renderer. The renderer
              is expected to be mostly idle during this period."/>
   <suffix name="Background.FifthMinute"
-      label="Time spent in tasks of a particular type during the fifth minute
-             after backgrounding the renderer."/>
+      label="Time spent in tasks of a particular queue type during the fifth
+             minute after backgrounding the renderer."/>
   <suffix name="Background.FirstMinute"
-      label="Time spent in tasks of a particular type during the first minute
-             after backgrounding the renderer. A large amount of loading
-             tasks are expected during this period."/>
+      label="Time spent in tasks of a particular queue type during the first
+             minute after backgrounding the renderer. A large amount of
+             loading tasks are expected during this period."/>
   <suffix name="Background.FourthMinute"
-      label="Time spent in tasks of a particular type during the fourth
+      label="Time spent in tasks of a particular queue type during the fourth
              minute after backgrounding the renderer."/>
   <suffix name="Background.KeepAlive.AfterFifthMinute"
-      label="Time spent in tasks of a particular type starting from the sixth
-             minute after backgrounding the renderer when keep-alive signal
-             is present."/>
+      label="Time spent in tasks of a particular queue type starting from the
+             sixth minute after backgrounding the renderer when keep-alive
+             signal is present."/>
   <suffix name="Background.KeepAlive.AfterTenthMinute"
-      label="Time spent in tasks of a particular type starting from the
+      label="Time spent in tasks of a particular queue type starting from the
              eleventh minute after backgrounding the renderer when keep-alive
              signal is present."/>
   <suffix name="Background.SecondMinute"
-      label="Time spent in tasks of a particular type during the second
+      label="Time spent in tasks of a particular queue type during the second
              minute after backgrounding the renderer."/>
   <suffix name="Background.ThirdMinute"
-      label="Time spent in tasks of a particular type during the third minute
-             after backgrounding the renderer."/>
+      label="Time spent in tasks of a particular queue type during the third
+             minute after backgrounding the renderer."/>
   <suffix name="Foreground"
-      label="Time spent in tasks of a particular type when the renderer is in
-             the foreground. Please note that individual tabs in this
+      label="Time spent in tasks of a particular queue type when the renderer
+             is in the foreground. Please note that individual tabs in this
              renderer can be backgrounded."/>
   <suffix name="Foreground.AfterThirdMinute"
-      label="Time spent in tasks of a particular type starting from the
+      label="Time spent in tasks of a particular queue type starting from the
              fourth minute after foregrounding the renderer."/>
   <suffix name="Foreground.FirstMinute"
-      label="Time spent in tasks of a particular type during the first minute
-             after foregrounding the renderer."/>
+      label="Time spent in tasks of a particular queue type during the first
+             minute after foregrounding the renderer."/>
   <suffix name="Foreground.SecondMinute"
-      label="Time spent in tasks of a particular type during the second
+      label="Time spent in tasks of a particular queue type during the second
              minute after foregrounding the renderer."/>
   <suffix name="Foreground.ThirdMinute"
-      label="Time spent in tasks of a particular type during the third minute
-             after foregrounding the renderer."/>
+      label="Time spent in tasks of a particular queue type during the third
+             minute after foregrounding the renderer."/>
   <suffix name="Hidden"
-      label="Time spent in tasks of a particular type when the renderer is
-             hidden."/>
+      label="Time spent in tasks of a particular queue type when the renderer
+             is hidden."/>
   <suffix name="HiddenMusic"
-      label="Time spent in tasks of a particular type when the renderer is
-             hidden and is playing audible sound."/>
+      label="Time spent in tasks of a particular queue type when the renderer
+             is hidden and is playing audible sound."/>
   <suffix name="Visible"
-      label="Time spent in tasks of a particular type when the renderer is
-             visible."/>
+      label="Time spent in tasks of a particular queue type when the renderer
+             is visible."/>
   <affected-histogram name="RendererScheduler.TaskDurationPerQueueType3"/>
 </histogram_suffixes>
 
diff --git a/tools/perf/page_sets/data/credentials.json.sha1 b/tools/perf/page_sets/data/credentials.json.sha1
index 981ac26e..f4677d0 100644
--- a/tools/perf/page_sets/data/credentials.json.sha1
+++ b/tools/perf/page_sets/data/credentials.json.sha1
@@ -1 +1 @@
-ef17bc387a772fdd588d05871bd3c993aaa61787
\ No newline at end of file
+62bb07f739452c44b3618b9b3f3de6251bf73322
\ No newline at end of file
diff --git a/tools/perf/page_sets/login_helpers/linkedin_login.py b/tools/perf/page_sets/login_helpers/linkedin_login.py
new file mode 100644
index 0000000..5843e14
--- /dev/null
+++ b/tools/perf/page_sets/login_helpers/linkedin_login.py
@@ -0,0 +1,41 @@
+# 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.
+
+from page_sets.login_helpers import login_utils
+
+
+def LoginDesktopAccount(action_runner, credential,
+                 credentials_path=login_utils.DEFAULT_CREDENTIAL_PATH):
+  """Logs in into a Linkedin account.
+
+  This function navigates the tab into Linkedin's login page and logs in a user
+  using credentials in |credential| part of the |credentials_path| file.
+
+  Args:
+    action_runner: Action runner responsible for running actions on the page.
+    credential: The credential to retrieve from the credentials file (string).
+    credentials_path: The path to credential file (string).
+
+  Raises:
+    exceptions.Error: See ExecuteJavaScript()
+    for a detailed list of possible exceptions.
+  """
+  account_name, password = login_utils.GetAccountNameAndPassword(
+      credential, credentials_path=credentials_path)
+
+  action_runner.Navigate('https://www.linkedin.com/uas/login')
+  action_runner.Wait(1) # Error page happens if this wait is not here.
+  login_utils.InputWithSelector(
+      action_runner, '%s@gmail.com' % account_name, 'input[type=text]')
+  login_utils.InputWithSelector(
+      action_runner, password, 'input[type=password]')
+
+  login_button_function = (
+      'document.getElementsByName("signin")[0]')
+  action_runner.WaitForElement(element_function=login_button_function)
+  action_runner.ClickElement(element_function=login_button_function)
+
+  search_bar_function = (
+      'document.getElementsByClassName("nav-search-bar")[0]')
+  action_runner.WaitForElement(element_function=search_bar_function)
diff --git a/ui/compositor/host/host_context_factory_private.cc b/ui/compositor/host/host_context_factory_private.cc
index 2242da97..dcea575d 100644
--- a/ui/compositor/host/host_context_factory_private.cc
+++ b/ui/compositor/host/host_context_factory_private.cc
@@ -4,6 +4,7 @@
 
 #include "ui/compositor/host/host_context_factory_private.h"
 
+#include "base/trace_event/trace_event.h"
 #include "build/build_config.h"
 #include "cc/mojo_embedder/async_layer_tree_frame_sink.h"
 #include "components/viz/client/hit_test_data_provider_draw_quad.h"
@@ -183,8 +184,8 @@
     // Otherwise when we return from WM_WINDOWPOSCHANGING message handler and
     // receive a WM_WINDOWPOSCHANGED the resize is finalized and any swaps of
     // wrong size by Viz can cause the swapped content to get scaled.
-    // TODO(samans): Investigate nonblocking ways for solving
-    // https://crbug.com/811945.
+    // TODO(crbug.com/859168): Investigate nonblocking ways for solving.
+    TRACE_EVENT0("viz", "Blocked UI for DisableSwapUntilResize");
     mojo::SyncCallRestrictions::ScopedAllowSyncCall scoped_allow_sync_call;
     iter->second.display_private->DisableSwapUntilResize();
   }
diff --git a/ui/login/display_manager.js b/ui/login/display_manager.js
index dab6e14..54fc62f 100644
--- a/ui/login/display_manager.js
+++ b/ui/login/display_manager.js
@@ -133,11 +133,10 @@
    * @type Array<Array<string>>
    * @const
    */
-  var SCREEN_GROUPS = [[SCREEN_OOBE_WELCOME,
-                        SCREEN_OOBE_EULA,
-                        SCREEN_OOBE_UPDATE,
-                        SCREEN_OOBE_AUTO_ENROLLMENT_CHECK]
-                      ];
+  var SCREEN_GROUPS = [[
+    SCREEN_OOBE_WELCOME, SCREEN_OOBE_EULA, SCREEN_OOBE_UPDATE,
+    SCREEN_OOBE_AUTO_ENROLLMENT_CHECK
+  ]];
   /**
    * Group of screens (screen IDs) where factory-reset screen invocation is
    * available.
@@ -436,16 +435,17 @@
           this.currentScreen.cancel();
         }
       } else if (name == ACCELERATOR_ENABLE_DEBBUGING) {
-        if (ENABLE_DEBUGGING_AVAILABLE_SCREEN_GROUP.indexOf(
-                currentStepId) != -1) {
+        if (ENABLE_DEBUGGING_AVAILABLE_SCREEN_GROUP.indexOf(currentStepId) !=
+            -1) {
           chrome.send('toggleEnableDebuggingScreen');
         }
       } else if (name == ACCELERATOR_ENROLLMENT) {
         if (currentStepId == SCREEN_GAIA_SIGNIN ||
             currentStepId == SCREEN_ACCOUNT_PICKER) {
           chrome.send('toggleEnrollmentScreen');
-        } else if (currentStepId == SCREEN_OOBE_WELCOME ||
-                   currentStepId == SCREEN_OOBE_EULA) {
+        } else if (
+            currentStepId == SCREEN_OOBE_WELCOME ||
+            currentStepId == SCREEN_OOBE_EULA) {
           // In this case update check will be skipped and OOBE will
           // proceed straight to enrollment screen when EULA is accepted.
           chrome.send('skipUpdateEnrollAfterEula');
@@ -460,8 +460,8 @@
           $('version-labels').hidden = !$('version-labels').hidden;
       } else if (name == ACCELERATOR_RESET) {
         if (currentStepId == SCREEN_OOBE_RESET)
-          $('reset').send(login.Screen.CALLBACK_USER_ACTED,
-                          USER_ACTION_ROLLBACK_TOGGLED);
+          $('reset').send(
+              login.Screen.CALLBACK_USER_ACTED, USER_ACTION_ROLLBACK_TOGGLED);
         else if (RESET_AVAILABLE_SCREEN_GROUP.indexOf(currentStepId) != -1)
           chrome.send('toggleResetScreen');
       } else if (name == ACCELERATOR_DEVICE_REQUISITION) {
@@ -579,8 +579,7 @@
 
       newStep.classList.remove('hidden');
 
-      if (this.isOobeUI() &&
-          this.screenIsAnimated_(nextStepId) &&
+      if (this.isOobeUI() && this.screenIsAnimated_(nextStepId) &&
           this.screenIsAnimated_(currentStepId)) {
         // Start gliding animation for OOBE steps.
         if (nextStepIndex > this.currentStep_) {
@@ -617,8 +616,7 @@
       if (nextStepId == SCREEN_GAIA_SIGNIN ||
           nextStepId == SCREEN_OOBE_ENROLLMENT) {
         newStep.setAttribute(
-            'aria-label',
-            loadTimeData.getString('signinScreenTitle'));
+            'aria-label', loadTimeData.getString('signinScreenTitle'));
       }
 
       // Default control to be focused (if specified).
@@ -657,18 +655,17 @@
         // First screen on OOBE launch.
         if (this.isOobeUI() && innerContainer.classList.contains('down')) {
           innerContainer.classList.remove('down');
-          innerContainer.addEventListener(
-              'transitionend', function f(e) {
-                innerContainer.removeEventListener('transitionend', f);
-                outerContainer.classList.remove('down');
-                chrome.send('loginVisible', ['oobe']);
-                // Refresh defaultControl. It could have changed.
-                var defaultControl = newStep.defaultControl;
-                if (defaultControl)
-                  defaultControl.focus();
-              });
-          ensureTransitionEndEvent(innerContainer,
-                                   MAX_SCREEN_TRANSITION_DURATION);
+          innerContainer.addEventListener('transitionend', function f(e) {
+            innerContainer.removeEventListener('transitionend', f);
+            outerContainer.classList.remove('down');
+            chrome.send('loginVisible', ['oobe']);
+            // Refresh defaultControl. It could have changed.
+            var defaultControl = newStep.defaultControl;
+            if (defaultControl)
+              defaultControl.focus();
+          });
+          ensureTransitionEndEvent(
+              innerContainer, MAX_SCREEN_TRANSITION_DURATION);
         } else {
           if (defaultControl)
             defaultControl.focus();
@@ -680,8 +677,7 @@
       $('step-logo').hidden = newStep.classList.contains('no-logo');
 
       $('oobe').dispatchEvent(
-          new CustomEvent('screenchanged',
-                          {detail: this.currentScreen.id}));
+          new CustomEvent('screenchanged', {detail: this.currentScreen.id}));
       chrome.send('updateCurrentScreen', [this.currentScreen.id]);
     },
 
@@ -798,11 +794,18 @@
           break;
         }
       }
-      $('inner-container').style.height = height + 'px';
-      $('inner-container').style.width = width + 'px';
+
+      if (screen.classList.contains('fullscreen')) {
+        $('inner-container').style.height = '100%';
+        $('inner-container').style.width = '100%';
+      } else {
+        $('inner-container').style.height = height + 'px';
+        $('inner-container').style.width = width + 'px';
+      }
       // This requires |screen| to have 'box-sizing: border-box'.
       screen.style.width = width + 'px';
       screen.style.height = height + 'px';
+      screen.style.margin = 'auto';
 
       if (this.showingViewsLogin) {
         chrome.send('updateGaiaDialogSize', [width, height]);
@@ -887,8 +890,8 @@
         screen.classList.remove('left');
       }
       if (this.showingViewsLogin) {
-        // Hide the shelf and version info because these should be displayed in
-        // views.
+        // Hide the shelf and version info because these should be
+        // displayed in views.
         $('login-header-bar').hidden = true;
         $('top-header-bar').hidden = true;
       }
@@ -923,7 +926,8 @@
     },
 
     /**
-     * Called when window size changed. Notifies current screen about change.
+     * Called when window size changed. Notifies current screen about
+     * change.
      * @private
      */
     onWindowResize_: function() {
@@ -935,7 +939,8 @@
     },
 
     /*
-     * Updates the device requisition string shown in the requisition prompt.
+     * Updates the device requisition string shown in the requisition
+     * prompt.
      * @param {string} requisition The device requisition.
      */
     updateDeviceRequisition: function(requisition) {
@@ -969,22 +974,21 @@
      * Shows the enable demo mode dialog.
      * @private
      */
-    showEnableDemoModeDialog_: function (promptText, requisition) {
+    showEnableDemoModeDialog_: function(promptText, requisition) {
       if (!this.enableDemoModeDialog_) {
         this.enableDemoModeDialog_ =
-          new cr.ui.dialogs.ConfirmDialog(document.body);
+            new cr.ui.dialogs.ConfirmDialog(document.body);
         this.enableDemoModeDialog_.setOkLabel(
-          loadTimeData.getString('enableDemoModeDialogConfirm'));
+            loadTimeData.getString('enableDemoModeDialogConfirm'));
         this.enableDemoModeDialog_.setCancelLabel(
-          loadTimeData.getString('enableDemoModeDialogCancel'));
+            loadTimeData.getString('enableDemoModeDialogCancel'));
       }
       this.enableDemoModeDialog_.showWithTitle(
-        loadTimeData.getString('enableDemoModeDialogTitle'),
-        loadTimeData.getString('enableDemoModeDialogText'),
-        function () {  // onOk
-          chrome.send('setupDemoMode');
-        },
-      );
+          loadTimeData.getString('enableDemoModeDialogTitle'),
+          loadTimeData.getString('enableDemoModeDialogText'),
+          function() {  // onOk
+            chrome.send('setupDemoMode');
+          }, );
     },
 
     /**
@@ -995,9 +999,9 @@
     },
 
     /**
-     * Sets or unsets given |className| for top-level container. Useful for
-     * customizing #inner-container with CSS rules. All classes set with with
-     * this method will be removed after screen change.
+     * Sets or unsets given |className| for top-level container. Useful
+     * for customizing #inner-container with CSS rules. All classes set
+     * with with this method will be removed after screen change.
      * @param {string} className Class to toggle.
      * @param {boolean} enabled Whether class should be enabled or disabled.
      */
@@ -1025,8 +1029,9 @@
       }
     });
     if (instance.displayType == DISPLAY_TYPE.UNKNOWN) {
-      console.error("Unknown display type '" + givenDisplayType +
-          "'. Setting default.");
+      console.error(
+          'Unknown display type "' + givenDisplayType +
+          '". Setting default.');
       instance.displayType = DISPLAY_TYPE.LOGIN;
     }
 
@@ -1058,10 +1063,12 @@
    */
   DisplayManager.getPosition = function(element) {
     var offset = DisplayManager.getOffset(element);
-    return { top: offset.top,
-             right: window.innerWidth - element.offsetWidth - offset.left,
-             bottom: window.innerHeight - element.offsetHeight - offset.top,
-             left: offset.left };
+    return {
+      top: offset.top,
+      right: window.innerWidth - element.offsetWidth - offset.left,
+      bottom: window.innerHeight - element.offsetHeight - offset.top,
+      left: offset.left
+    };
   };
 
   /**
@@ -1094,8 +1101,8 @@
     var currentScreenId = Oobe.getInstance().currentScreen.id;
 
     if ($(SCREEN_GAIA_SIGNIN))
-      $(SCREEN_GAIA_SIGNIN).reset(
-          currentScreenId == SCREEN_GAIA_SIGNIN, forceOnline);
+      $(SCREEN_GAIA_SIGNIN)
+          .reset(currentScreenId == SCREEN_GAIA_SIGNIN, forceOnline);
     $('login-header-bar').disabled = false;
     $('pod-row').reset(currentScreenId == SCREEN_ACCOUNT_PICKER);
   };
@@ -1248,8 +1255,9 @@
    * @param {string} assetId The device asset ID.
    */
   DisplayManager.setEnterpriseInfo = function(messageText, assetId) {
-    $('asset-id').textContent = ((assetId == "") ? "" :
-        loadTimeData.getStringF('assetIdLabel', assetId));
+    $('asset-id').textContent =
+        ((assetId == '') ? '' :
+                           loadTimeData.getStringF('assetIdLabel', assetId));
   };
 
   /**
@@ -1265,18 +1273,19 @@
    * Disable Add users button if said.
    * @param {boolean} disable true to disable
    */
-  DisplayManager.updateAddUserButtonStatus = function(disable) {
+  DisplayManager.updateAddUserButtonStatus =
+      function(disable) {
     $('add-user-button').disabled = disable;
-    $('add-user-button').classList[
-        disable ? 'add' : 'remove']('button-restricted');
-    $('add-user-button').title = disable ?
-        loadTimeData.getString('disabledAddUserTooltip') : '';
+    $('add-user-button')
+        .classList[disable ? 'add' : 'remove']('button-restricted');
+    $('add-user-button').title =
+        disable ? loadTimeData.getString('disabledAddUserTooltip') : '';
   }
 
-  /**
-   * Clears password field in user-pod.
-   */
-  DisplayManager.clearUserPodPassword = function() {
+      /**
+       * Clears password field in user-pod.
+       */
+      DisplayManager.clearUserPodPassword = function() {
     $('pod-row').clearFocusedPod();
   };
 
diff --git a/ui/ozone/demo/vulkan_overlay_renderer.cc b/ui/ozone/demo/vulkan_overlay_renderer.cc
index d292d5a..3baa93a 100644
--- a/ui/ozone/demo/vulkan_overlay_renderer.cc
+++ b/ui/ozone/demo/vulkan_overlay_renderer.cc
@@ -43,7 +43,6 @@
     : RendererBase(widget, size),
       surface_factory_ozone_(surface_factory_ozone),
       vulkan_implementation_(vulkan_implementation),
-      vulkan_function_pointers_(gpu::GetVulkanFunctionPointers()),
       overlay_surface_(std::move(overlay_surface)),
       weak_ptr_factory_(this) {}
 
@@ -98,9 +97,8 @@
       .dependencyCount = 0,
   };
 
-  CHECK_EQ(vulkan_function_pointers_->vkCreateRenderPass(
-               device_queue_->GetVulkanDevice(), &render_pass_create_info,
-               nullptr, &render_pass_),
+  CHECK_EQ(vkCreateRenderPass(device_queue_->GetVulkanDevice(),
+                              &render_pass_create_info, nullptr, &render_pass_),
            VK_SUCCESS);
 
   command_pool_ = std::make_unique<gpu::VulkanCommandPool>(device_queue_.get());
@@ -122,14 +120,10 @@
   for (std::unique_ptr<Buffer>& buffer : buffers_) {
     if (!buffer)
       continue;
-    vulkan_function_pointers_->vkDestroyFramebuffer(
-        vk_device, buffer->vk_framebuffer(), nullptr);
-    vulkan_function_pointers_->vkDestroyImageView(
-        vk_device, buffer->vk_image_view(), nullptr);
-    vulkan_function_pointers_->vkDestroyImage(vk_device, buffer->vk_image(),
-                                              nullptr);
-    vulkan_function_pointers_->vkFreeMemory(
-        vk_device, buffer->vk_device_memory(), nullptr);
+    vkDestroyFramebuffer(vk_device, buffer->vk_framebuffer(), nullptr);
+    vkDestroyImageView(vk_device, buffer->vk_image_view(), nullptr);
+    vkDestroyImage(vk_device, buffer->vk_image(), nullptr);
+    vkFreeMemory(vk_device, buffer->vk_device_memory(), nullptr);
     buffer.reset();
   }
 }
@@ -140,9 +134,9 @@
   DestroyBuffers();
 
   for (auto& buffer : buffers_) {
-    buffer = Buffer::Create(surface_factory_ozone_, vulkan_function_pointers_,
-                            device_queue_->GetVulkanDevice(), render_pass_,
-                            widget_, size_);
+    buffer =
+        Buffer::Create(surface_factory_ozone_, device_queue_->GetVulkanDevice(),
+                       render_pass_, widget_, size_);
     CHECK(buffer);
   }
 }
@@ -171,18 +165,18 @@
     begin_info.clearValueCount = 1;
     begin_info.pClearValues = &clear_value;
 
-    vulkan_function_pointers_->vkCmdBeginRenderPass(
-        recorder.handle(), &begin_info, VK_SUBPASS_CONTENTS_INLINE);
+    vkCmdBeginRenderPass(recorder.handle(), &begin_info,
+                         VK_SUBPASS_CONTENTS_INLINE);
 
-    vulkan_function_pointers_->vkCmdEndRenderPass(recorder.handle());
+    vkCmdEndRenderPass(recorder.handle());
   }
 
   VkSemaphoreCreateInfo sem_create_info = {
       .sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO,
   };
   VkSemaphore semaphore;
-  vulkan_function_pointers_->vkCreateSemaphore(
-      device_queue_->GetVulkanDevice(), &sem_create_info, NULL, &semaphore);
+  vkCreateSemaphore(device_queue_->GetVulkanDevice(), &sem_create_info, NULL,
+                    &semaphore);
 
   command_buffer_->Submit(1, &semaphore, 0, nullptr);
 
@@ -264,7 +258,6 @@
 std::unique_ptr<VulkanOverlayRenderer::Buffer>
 VulkanOverlayRenderer::Buffer::Create(
     SurfaceFactoryOzone* surface_factory_ozone,
-    const gpu::VulkanFunctionPointers* vulkan_function_pointers,
     VkDevice vk_device,
     VkRenderPass vk_render_pass,
     gfx::AcceleratedWidget widget,
@@ -275,8 +268,8 @@
   VkDeviceMemory vk_device_memory = VK_NULL_HANDLE;
   scoped_refptr<gfx::NativePixmap> native_pixmap =
       surface_factory_ozone->CreateNativePixmapForVulkan(
-          widget, size, format, gfx::BufferUsage::SCANOUT,
-          vulkan_function_pointers, vk_device, &vk_device_memory, &vk_image);
+          widget, size, format, gfx::BufferUsage::SCANOUT, vk_device,
+          &vk_device_memory, &vk_image);
   if (!native_pixmap) {
     LOG(FATAL)
         << "Failed to create a presentable buffer for rendering with vulkan";
@@ -306,8 +299,8 @@
 
   VkResult result;
   VkImageView vk_image_view = VK_NULL_HANDLE;
-  result = vulkan_function_pointers->vkCreateImageView(
-      vk_device, &vk_image_view_create_info, nullptr, &vk_image_view);
+  result = vkCreateImageView(vk_device, &vk_image_view_create_info, nullptr,
+                             &vk_image_view);
   if (result != VK_SUCCESS) {
     LOG(FATAL) << "Failed to create a Vulkan image view.";
   }
@@ -322,8 +315,8 @@
   };
 
   VkFramebuffer vk_framebuffer = VK_NULL_HANDLE;
-  result = vulkan_function_pointers->vkCreateFramebuffer(
-      vk_device, &vk_framebuffer_create_info, nullptr, &vk_framebuffer);
+  result = vkCreateFramebuffer(vk_device, &vk_framebuffer_create_info, nullptr,
+                               &vk_framebuffer);
   if (result != VK_SUCCESS) {
     LOG(FATAL) << "Failed to create a Vulkan framebuffer.";
   }
diff --git a/ui/ozone/demo/vulkan_overlay_renderer.h b/ui/ozone/demo/vulkan_overlay_renderer.h
index 7bf290b..d1c0fa55 100644
--- a/ui/ozone/demo/vulkan_overlay_renderer.h
+++ b/ui/ozone/demo/vulkan_overlay_renderer.h
@@ -10,7 +10,6 @@
 #include "base/macros.h"
 #include "base/memory/ref_counted.h"
 #include "base/memory/weak_ptr.h"
-#include "gpu/vulkan/vulkan_function_pointers.h"
 #include "ui/gfx/buffer_types.h"
 #include "ui/gfx/presentation_feedback.h"
 #include "ui/gfx/swap_result.h"
@@ -65,7 +64,6 @@
 
     static std::unique_ptr<Buffer> Create(
         SurfaceFactoryOzone* surface_factory_ozone,
-        const gpu::VulkanFunctionPointers* vulkan_function_pointers,
         VkDevice vk_device,
         VkRenderPass vk_render_pass,
         gfx::AcceleratedWidget widget,
@@ -96,7 +94,6 @@
 
   SurfaceFactoryOzone* const surface_factory_ozone_;
   gpu::VulkanImplementation* const vulkan_implementation_;
-  const gpu::VulkanFunctionPointers* const vulkan_function_pointers_;
   std::unique_ptr<gpu::VulkanDeviceQueue> device_queue_;
   std::unique_ptr<gpu::VulkanCommandPool> command_pool_;
   std::unique_ptr<gpu::VulkanCommandBuffer> command_buffer_;
diff --git a/ui/ozone/platform/drm/gpu/gbm_surface_factory.cc b/ui/ozone/platform/drm/gpu/gbm_surface_factory.cc
index 34b60ea..bdd2431 100644
--- a/ui/ozone/platform/drm/gpu/gbm_surface_factory.cc
+++ b/ui/ozone/platform/drm/gpu/gbm_surface_factory.cc
@@ -145,7 +145,6 @@
     gfx::Size size,
     gfx::BufferFormat format,
     gfx::BufferUsage usage,
-    const gpu::VulkanFunctionPointers* vulkan_function_pointers,
     VkDevice vk_device,
     VkDeviceMemory* vk_device_memory,
     VkImage* vk_image) {
@@ -157,8 +156,7 @@
 
   PFN_vkCreateDmaBufImageINTEL create_dma_buf_image_intel =
       reinterpret_cast<PFN_vkCreateDmaBufImageINTEL>(
-          vulkan_function_pointers->vkGetDeviceProcAddr(
-              vk_device, "vkCreateDmaBufImageINTEL"));
+          vkGetDeviceProcAddr(vk_device, "vkCreateDmaBufImageINTEL"));
   if (!create_dma_buf_image_intel) {
     LOG(ERROR) << "Scanout buffers can only be imported into vulkan when "
                   "vkCreateDmaBufImageINTEL is available.";
diff --git a/ui/ozone/platform/drm/gpu/gbm_surface_factory.h b/ui/ozone/platform/drm/gpu/gbm_surface_factory.h
index edddd2c4..469e939a 100644
--- a/ui/ozone/platform/drm/gpu/gbm_surface_factory.h
+++ b/ui/ozone/platform/drm/gpu/gbm_surface_factory.h
@@ -46,7 +46,6 @@
       gfx::Size size,
       gfx::BufferFormat format,
       gfx::BufferUsage usage,
-      const gpu::VulkanFunctionPointers* vulkan_function_pointers,
       VkDevice vk_device,
       VkDeviceMemory* vk_device_memory,
       VkImage* vk_image) override;
diff --git a/ui/ozone/platform/drm/gpu/vulkan_implementation_gbm.cc b/ui/ozone/platform/drm/gpu/vulkan_implementation_gbm.cc
index 55499c9..b5173934 100644
--- a/ui/ozone/platform/drm/gpu/vulkan_implementation_gbm.cc
+++ b/ui/ozone/platform/drm/gpu/vulkan_implementation_gbm.cc
@@ -38,7 +38,7 @@
 
   vkGetPhysicalDeviceExternalFencePropertiesKHR_ =
       reinterpret_cast<PFN_vkGetPhysicalDeviceExternalFencePropertiesKHR>(
-          vulkan_function_pointers->vkGetInstanceProcAddr(
+          vkGetInstanceProcAddr(
               vulkan_instance_.vk_instance(),
               "vkGetPhysicalDeviceExternalFencePropertiesKHR"));
   if (!vkGetPhysicalDeviceExternalFencePropertiesKHR_) {
@@ -47,8 +47,7 @@
   }
 
   vkGetFenceFdKHR_ = reinterpret_cast<PFN_vkGetFenceFdKHR>(
-      vulkan_function_pointers->vkGetInstanceProcAddr(
-          vulkan_instance_.vk_instance(), "vkGetFenceFdKHR"));
+      vkGetInstanceProcAddr(vulkan_instance_.vk_instance(), "vkGetFenceFdKHR"));
   if (!vkGetFenceFdKHR_) {
     vulkan_instance_.Destroy();
     return false;
@@ -101,8 +100,8 @@
       VK_EXTERNAL_FENCE_HANDLE_TYPE_SYNC_FD_BIT;
 
   VkFence fence;
-  VkResult result = gpu::GetVulkanFunctionPointers()->vkCreateFence(
-      vk_device, &fence_create_info, nullptr, &fence);
+  VkResult result =
+      vkCreateFence(vk_device, &fence_create_info, nullptr, &fence);
   if (result != VK_SUCCESS) {
     DLOG(ERROR) << "vkCreateFence failed: " << result;
     return VK_NULL_HANDLE;
diff --git a/ui/ozone/platform/drm/gpu/vulkan_implementation_gbm.h b/ui/ozone/platform/drm/gpu/vulkan_implementation_gbm.h
index 400cc421..c619196 100644
--- a/ui/ozone/platform/drm/gpu/vulkan_implementation_gbm.h
+++ b/ui/ozone/platform/drm/gpu/vulkan_implementation_gbm.h
@@ -28,7 +28,7 @@
       uint32_t queue_family_index) override;
   std::vector<const char*> GetRequiredDeviceExtensions() override;
   VkFence CreateVkFenceForGpuFence(VkDevice vk_device) override;
-  virtual std::unique_ptr<gfx::GpuFence> ExportVkFenceToGpuFence(
+  std::unique_ptr<gfx::GpuFence> ExportVkFenceToGpuFence(
       VkDevice vk_device,
       VkFence vk_fence) override;
 
diff --git a/ui/ozone/public/surface_factory_ozone.cc b/ui/ozone/public/surface_factory_ozone.cc
index 873eee3..4d99ecb 100644
--- a/ui/ozone/public/surface_factory_ozone.cc
+++ b/ui/ozone/public/surface_factory_ozone.cc
@@ -43,7 +43,6 @@
     gfx::Size size,
     gfx::BufferFormat format,
     gfx::BufferUsage usage,
-    const gpu::VulkanFunctionPointers* vulkan_function_pointers,
     VkDevice vk_device,
     VkDeviceMemory* vk_device_memory,
     VkImage* vk_image) {
diff --git a/ui/ozone/public/surface_factory_ozone.h b/ui/ozone/public/surface_factory_ozone.h
index cdcaae3e..af51cfa 100644
--- a/ui/ozone/public/surface_factory_ozone.h
+++ b/ui/ozone/public/surface_factory_ozone.h
@@ -32,10 +32,6 @@
 class NativePixmap;
 }
 
-namespace gpu {
-struct VulkanFunctionPointers;
-}
-
 namespace ui {
 
 class SurfaceOzoneCanvas;
@@ -93,7 +89,6 @@
       gfx::Size size,
       gfx::BufferFormat format,
       gfx::BufferUsage usage,
-      const gpu::VulkanFunctionPointers* vulkan_function_pointers,
       VkDevice vk_device,
       VkDeviceMemory* vk_device_memory,
       VkImage* vk_image);
diff --git a/ui/views/widget/desktop_aura/desktop_window_tree_host_x11.cc b/ui/views/widget/desktop_aura/desktop_window_tree_host_x11.cc
index acf2d29..438e596 100644
--- a/ui/views/widget/desktop_aura/desktop_window_tree_host_x11.cc
+++ b/ui/views/widget/desktop_aura/desktop_window_tree_host_x11.cc
@@ -1549,6 +1549,8 @@
   // SetWMSpecState) has no effect here since the window has not yet been
   // mapped. So we manually change the state.
   if (!state_atom_list.empty()) {
+    DCHECK(window_properties_in_client_.empty());
+    window_properties_in_client_ = state_atom_list;
     ui::SetAtomArrayProperty(xwindow_,
                              "_NET_WM_STATE",
                              "ATOM",