diff --git a/BUILD.gn b/BUILD.gn
index 0be93385..d354f62 100644
--- a/BUILD.gn
+++ b/BUILD.gn
@@ -163,10 +163,8 @@
       "//media/midi:midi_unittests",
       "//media/mojo:media_mojo_unittests",
       "//mojo",
+      "//mojo:mojo_unittests",
       "//mojo/common:mojo_common_unittests",
-      "//mojo/edk/system:mojo_system_unittests",
-      "//mojo/edk/test:mojo_public_bindings_unittests",
-      "//mojo/edk/test:mojo_public_system_unittests",
       "//net:net_perftests",
       "//third_party/WebKit/Source/controller:webkit_unit_tests",
       "//third_party/WebKit/Source/platform/wtf:wtf_unittests",
@@ -375,6 +373,7 @@
       "//ash:ash_unittests",
       "//chromeos:chromeos_unittests",
       "//chromeos/components:chromeos_components_unittests",
+      "//chromeos/services:chromeos_services_unittests",
       "//components/session_manager/core",
       "//ui/app_list:app_list_demo",
       "//ui/app_list:app_list_unittests",
diff --git a/DEPS b/DEPS
index eb65035..63b422e 100644
--- a/DEPS
+++ b/DEPS
@@ -79,11 +79,11 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling Skia
   # and whatever else without interference from each other.
-  'skia_revision': '6ede412188278e3b9529fe4b486f3b4899c922d0',
+  'skia_revision': '242135a402592e4fb40c5aba44cf8d483e68d292',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling V8
   # and whatever else without interference from each other.
-  'v8_revision': 'b00d05802cf53775ccdfb441e2494d99d3188ac9',
+  'v8_revision': '9421a28564d496ea757f5dd54f7d7065163f05bd',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling swarming_client
   # and whatever else without interference from each other.
@@ -91,7 +91,7 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling ANGLE
   # and whatever else without interference from each other.
-  'angle_revision': '24842908c4bd011178d01be6e61000a166f822fd',
+  'angle_revision': '1dce1bd38e1b89c41b28e7e980643651670b595a',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling build tools
   # and whatever else without interference from each other.
@@ -103,7 +103,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': 'aa2aff78e082f14e4bc418f68b27817f90e3f07a',
+  'pdfium_revision': '3760afa05acd4955120a47b82ecf3d543541b791',
   # 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.
@@ -135,7 +135,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': '922be470d304acb298558e74a555ad41a0c9bed4',
+  'catapult_revision': 'aa397a9110bb422abf77e87fbd4592c9de26b5af',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling libFuzzer
   # and whatever else without interference from each other.
@@ -156,6 +156,7 @@
   'android.googlesource.com',
   'aomedia.googlesource.com',
   'boringssl.googlesource.com',
+  'chrome-infra-packages.appspot.com',
   'chrome-internal.googlesource.com',
   'chromium.googlesource.com',
   'pdfium.googlesource.com',
@@ -262,7 +263,7 @@
     Var('chromium_git') + '/external/github.com/KhronosGroup/SPIRV-Tools.git' + '@' + '9166854ac93ef81b026e943ccd230fed6c8b8d3c',
 
   'src/third_party/android_protobuf/src': {
-      'url': Var('chromium_git') + '/external/android_protobuf.git' + '@' + '7fca48d8ce97f7ba3ab8eea5c472f1ad3711762f',
+      'url': Var('android_git') + '/platform/external/protobuf.git' + '@' + '7fca48d8ce97f7ba3ab8eea5c472f1ad3711762f',
       'condition': 'checkout_android',
   },
 
@@ -311,7 +312,7 @@
 
   # Build tools for Chrome OS. Note: This depends on third_party/pyelftools.
   'src/third_party/chromite': {
-      'url': Var('chromium_git') + '/chromiumos/chromite.git' + '@' + '7dd3b4e6d7a6b2e123d1aec04b121fd7c40871e0',
+      'url': Var('chromium_git') + '/chromiumos/chromite.git' + '@' + 'c1049d9106425476b49ba099e7691330fa117d6c',
       'condition': 'checkout_linux',
   },
 
@@ -326,17 +327,17 @@
 
   # For Linux and Chromium OS.
   'src/third_party/cros_system_api': {
-      'url': Var('chromium_git') + '/chromiumos/platform/system_api.git' + '@' + '13139639690bd5fddc221333d7a314b22559efc3',
+      'url': Var('chromium_git') + '/chromiumos/platform/system_api.git' + '@' + '7d15090bd03615b00e6ad070da5e85c0aa76aa13',
       'condition': 'checkout_linux',
   },
 
   'src/third_party/custom_tabs_client/src': {
-      'url': Var('chromium_git') + '/custom-tabs-client.git' + '@' + 'a0b6c6f7db4127c191ebb9ef008609224658edb2',
+      'url': Var('chromium_git') + '/custom-tabs-client.git' + '@' + '9cfda16f07de5d34c2cce32da389612ae673d235',
       'condition': 'checkout_android',
   },
 
   'src/third_party/depot_tools':
-    Var('chromium_git') + '/chromium/tools/depot_tools.git' + '@' + '543dc3e4c34ee236dd6e40d7f92be28a28410480',
+    Var('chromium_git') + '/chromium/tools/depot_tools.git' + '@' + 'f9648b5c7cf801b45bbdc0bf2d7dc71e4f047584',
 
   'src/third_party/devtools-node-modules':
     Var('chromium_git') + '/external/github.com/ChromeDevTools/devtools-node-modules' + '@' + Var('devtools_node_modules_revision'),
@@ -647,7 +648,7 @@
     Var('chromium_git') + '/external/khronosgroup/webgl.git' + '@' + 'a182a9ad3078aca566d8355eabf2d9f56f70ee82',
 
   'src/third_party/webrtc':
-    Var('webrtc_git') + '/src.git' + '@' + '5283022790de569cd0792fbd9d434f1fb56ce289', # commit position 21742
+    Var('webrtc_git') + '/src.git' + '@' + '35dd6cd88ad822d388ae44d706d5b94a08f038bf', # commit position 21742
 
   '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 68397e6..d436c3b 100644
--- a/android_webview/BUILD.gn
+++ b/android_webview/BUILD.gn
@@ -987,12 +987,14 @@
   deps = [
     ":android_webview_commandline_java",
     ":android_webview_platform_services_java",
+    ":system_webview_manifest",
     "//base:base_java",
     "//components/background_task_scheduler:background_task_scheduler_java",
     "//components/minidump_uploader:minidump_uploader_java",
   ]
 
   srcjar_deps = [ ":crash_receiver_aidl" ]
+  android_manifest_for_lint = system_webview_android_manifest
 }
 
 android_aidl("crash_receiver_aidl") {
diff --git a/android_webview/browser/aw_javascript_dialog_manager.cc b/android_webview/browser/aw_javascript_dialog_manager.cc
index 3e4a3be1..b292468 100644
--- a/android_webview/browser/aw_javascript_dialog_manager.cc
+++ b/android_webview/browser/aw_javascript_dialog_manager.cc
@@ -8,6 +8,7 @@
 
 #include "android_webview/browser/aw_contents_client_bridge.h"
 #include "content/public/browser/javascript_dialog_manager.h"
+#include "content/public/browser/render_frame_host.h"
 #include "content/public/browser/web_contents.h"
 
 namespace android_webview {
@@ -18,7 +19,7 @@
 
 void AwJavaScriptDialogManager::RunJavaScriptDialog(
     content::WebContents* web_contents,
-    const GURL& origin_url,
+    content::RenderFrameHost* render_frame_host,
     content::JavaScriptDialogType dialog_type,
     const base::string16& message_text,
     const base::string16& default_prompt_text,
@@ -31,8 +32,9 @@
     return;
   }
 
-  bridge->RunJavaScriptDialog(dialog_type, origin_url, message_text,
-                              default_prompt_text, std::move(callback));
+  bridge->RunJavaScriptDialog(
+      dialog_type, render_frame_host->GetLastCommittedURL(), message_text,
+      default_prompt_text, std::move(callback));
 }
 
 void AwJavaScriptDialogManager::RunBeforeUnloadDialog(
diff --git a/android_webview/browser/aw_javascript_dialog_manager.h b/android_webview/browser/aw_javascript_dialog_manager.h
index e31c967..c22258c 100644
--- a/android_webview/browser/aw_javascript_dialog_manager.h
+++ b/android_webview/browser/aw_javascript_dialog_manager.h
@@ -17,7 +17,7 @@
 
   // Overridden from content::JavaScriptDialogManager:
   void RunJavaScriptDialog(content::WebContents* web_contents,
-                           const GURL& origin_url,
+                           content::RenderFrameHost* render_frame_host,
                            content::JavaScriptDialogType dialog_type,
                            const base::string16& message_text,
                            const base::string16& default_prompt_text,
diff --git a/android_webview/browser/aw_safe_browsing_whitelist_manager.cc b/android_webview/browser/aw_safe_browsing_whitelist_manager.cc
index 873eea2..15990238 100644
--- a/android_webview/browser/aw_safe_browsing_whitelist_manager.cc
+++ b/android_webview/browser/aw_safe_browsing_whitelist_manager.cc
@@ -231,8 +231,7 @@
 
 bool AwSafeBrowsingWhitelistManager::IsURLWhitelisted(const GURL& url) const {
   DCHECK(io_task_runner_->RunsTasksInCurrentSequence());
-  if (!(url.SchemeIsHTTPOrHTTPS() || url.SchemeIsWSOrWSS() ||
-        url.SchemeIs(url::kFtpScheme))) {
+  if (!url.has_host()) {
     return false;
   }
   return IsWhitelisted(url, whitelist_.get());
diff --git a/android_webview/browser/aw_safe_browsing_whitelist_manager_unittest.cc b/android_webview/browser/aw_safe_browsing_whitelist_manager_unittest.cc
index b3097b8..e862e46 100644
--- a/android_webview/browser/aw_safe_browsing_whitelist_manager_unittest.cc
+++ b/android_webview/browser/aw_safe_browsing_whitelist_manager_unittest.cc
@@ -475,13 +475,28 @@
   EXPECT_FALSE(wm_->IsURLWhitelisted(GURL("http://com/")));
 }
 
-TEST_F(AwSafeBrowsingWhitelistManagerTest,
-       VerifyNonWsNonHttpSchemeInUrlsAreNotWhitelisted) {
+TEST_F(AwSafeBrowsingWhitelistManagerTest, VerifyInvalidUrlsAreNotWhitelisted) {
   std::vector<std::string> whitelist;
   whitelist.push_back("google.com");
   SetWhitelist(std::move(whitelist), true);
   base::RunLoop().RunUntilIdle();
-  EXPECT_FALSE(wm_->IsURLWhitelisted(GURL("file://a/b/test")));
+
+  GURL url = GURL("");
+  EXPECT_FALSE(url.is_valid());
+  EXPECT_FALSE(wm_->IsURLWhitelisted(url));
+
+  url = GURL("http;??www.google.com");
+  EXPECT_FALSE(url.is_valid());
+  EXPECT_FALSE(wm_->IsURLWhitelisted(url));
+}
+
+TEST_F(AwSafeBrowsingWhitelistManagerTest,
+       VerifyUrlsWithoutHostAreNotWhitelisted) {
+  std::vector<std::string> whitelist;
+  whitelist.push_back("google.com");
+  SetWhitelist(std::move(whitelist), true);
+  base::RunLoop().RunUntilIdle();
+  EXPECT_FALSE(wm_->IsURLWhitelisted(GURL("file:///google.com/test")));
   EXPECT_FALSE(wm_->IsURLWhitelisted(GURL("mailto:google.com/")));
   EXPECT_FALSE(wm_->IsURLWhitelisted(GURL("data:google.com/")));
 
diff --git a/android_webview/glue/java/src/com/android/webview/chromium/ContentSettingsAdapter.java b/android_webview/glue/java/src/com/android/webview/chromium/ContentSettingsAdapter.java
index 8321c83..6a555b2 100644
--- a/android_webview/glue/java/src/com/android/webview/chromium/ContentSettingsAdapter.java
+++ b/android_webview/glue/java/src/com/android/webview/chromium/ContentSettingsAdapter.java
@@ -4,7 +4,6 @@
 
 package com.android.webview.chromium;
 
-import android.annotation.TargetApi;
 import android.os.Build;
 import android.webkit.WebSettings.LayoutAlgorithm;
 import android.webkit.WebSettings.PluginState;
@@ -18,7 +17,6 @@
  * {@link org.chromium.android_webview.AwSettings}.
  */
 @SuppressWarnings({"deprecation", "NoSynchronizedMethodCheck"})
-@TargetApi(Build.VERSION_CODES.LOLLIPOP)
 public class ContentSettingsAdapter extends android.webkit.WebSettings {
     private AwSettings mAwSettings;
 
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 41ffd3d..94d36c2 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
@@ -144,7 +144,6 @@
     @Override
     // BUG=6790250 |javaScriptInterfaces| was only ever used by the obsolete DumpRenderTree
     // so is ignored. TODO: remove it from WebViewProvider.
-    @TargetApi(Build.VERSION_CODES.LOLLIPOP)
     public void init(final Map<String, Object> javaScriptInterfaces,
             final boolean privateBrowsing) {
         if (privateBrowsing) {
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 f40af7b..2e965e0 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
@@ -88,8 +88,6 @@
  * choose the latter, because it makes for a cleaner design.
  */
 @SuppressWarnings("deprecation")
-// You shouldn't change TargetApi, please see how Android M API was added.
-@TargetApi(Build.VERSION_CODES.LOLLIPOP)
 class WebViewContentsClientAdapter extends AwContentsClient {
     // TAG is chosen for consistency with classic webview tracing.
     private static final String TAG = "WebViewCallback";
diff --git a/android_webview/glue/java/src/com/android/webview/chromium/WebViewDelegateFactory.java b/android_webview/glue/java/src/com/android/webview/chromium/WebViewDelegateFactory.java
index 085c646..8a3827b6 100644
--- a/android_webview/glue/java/src/com/android/webview/chromium/WebViewDelegateFactory.java
+++ b/android_webview/glue/java/src/com/android/webview/chromium/WebViewDelegateFactory.java
@@ -4,7 +4,6 @@
 
 package com.android.webview.chromium;
 
-import android.annotation.TargetApi;
 import android.app.Application;
 import android.content.Context;
 import android.content.ContextWrapper;
@@ -12,7 +11,6 @@
 import android.content.res.AssetManager;
 import android.content.res.Resources;
 import android.graphics.Canvas;
-import android.os.Build;
 import android.os.Trace;
 import android.util.SparseArray;
 import android.view.View;
@@ -227,7 +225,6 @@
      * reflection to call into hidden frameworks APIs released in the API-21 version of the
      * framework.
      */
-    @TargetApi(Build.VERSION_CODES.LOLLIPOP)
     private static class Api21CompatibilityDelegate implements WebViewDelegate {
         /** Copy of Trace.TRACE_TAG_WEBVIEW */
         private static final long TRACE_TAG_WEBVIEW = 1L << 4;
diff --git a/android_webview/java/src/org/chromium/android_webview/AwWebContentsDelegateAdapter.java b/android_webview/java/src/org/chromium/android_webview/AwWebContentsDelegateAdapter.java
index 1b20c3c..1a0d5d5ba 100644
--- a/android_webview/java/src/org/chromium/android_webview/AwWebContentsDelegateAdapter.java
+++ b/android_webview/java/src/org/chromium/android_webview/AwWebContentsDelegateAdapter.java
@@ -5,12 +5,10 @@
 package org.chromium.android_webview;
 
 import android.annotation.SuppressLint;
-import android.annotation.TargetApi;
 import android.content.Context;
 import android.media.AudioManager;
 import android.net.Uri;
 import android.os.AsyncTask;
-import android.os.Build;
 import android.os.Handler;
 import android.os.Message;
 import android.provider.MediaStore;
@@ -34,7 +32,6 @@
  * This class also serves a secondary function of routing certain callbacks from the content layer
  * to specific listener interfaces.
  */
-@TargetApi(Build.VERSION_CODES.LOLLIPOP)
 class AwWebContentsDelegateAdapter extends AwWebContentsDelegate {
     private static final String TAG = "AwWebContentsDelegateAdapter";
 
diff --git a/android_webview/java/src/org/chromium/android_webview/crash/AwMinidumpUploadJobService.java b/android_webview/java/src/org/chromium/android_webview/crash/AwMinidumpUploadJobService.java
index 52e3365..e6dd1c93 100644
--- a/android_webview/java/src/org/chromium/android_webview/crash/AwMinidumpUploadJobService.java
+++ b/android_webview/java/src/org/chromium/android_webview/crash/AwMinidumpUploadJobService.java
@@ -3,8 +3,6 @@
 // found in the LICENSE file.
 package org.chromium.android_webview.crash;
 
-import android.annotation.TargetApi;
-import android.os.Build;
 import android.os.PersistableBundle;
 
 import org.chromium.android_webview.command_line.CommandLineUtil;
@@ -16,7 +14,6 @@
 /**
  * Class that interacts with the Android JobScheduler to upload Minidumps at appropriate times.
  */
-@TargetApi(Build.VERSION_CODES.LOLLIPOP)
 // OBS: This class needs to be public to be started from android.app.ActivityThread.
 public class AwMinidumpUploadJobService extends MinidumpUploadJobService {
     @Override
diff --git a/android_webview/java/src/org/chromium/android_webview/crash/CrashReceiverService.java b/android_webview/java/src/org/chromium/android_webview/crash/CrashReceiverService.java
index 4ee948ab..fb16977 100644
--- a/android_webview/java/src/org/chromium/android_webview/crash/CrashReceiverService.java
+++ b/android_webview/java/src/org/chromium/android_webview/crash/CrashReceiverService.java
@@ -4,13 +4,11 @@
 
 package org.chromium.android_webview.crash;
 
-import android.annotation.TargetApi;
 import android.app.Service;
 import android.app.job.JobInfo;
 import android.content.ComponentName;
 import android.content.Intent;
 import android.os.Binder;
-import android.os.Build;
 import android.os.IBinder;
 import android.os.ParcelFileDescriptor;
 
@@ -27,7 +25,6 @@
 /**
  * Service that is responsible for receiving crash dumps from an application, for upload.
  */
-@TargetApi(Build.VERSION_CODES.LOLLIPOP)
 public class CrashReceiverService extends Service {
     private static final String TAG = "CrashReceiverService";
 
diff --git a/android_webview/java/src/org/chromium/android_webview/variations/AwVariationsConfigurationService.java b/android_webview/java/src/org/chromium/android_webview/variations/AwVariationsConfigurationService.java
index e48f6fc..5771d5be 100644
--- a/android_webview/java/src/org/chromium/android_webview/variations/AwVariationsConfigurationService.java
+++ b/android_webview/java/src/org/chromium/android_webview/variations/AwVariationsConfigurationService.java
@@ -4,14 +4,12 @@
 
 package org.chromium.android_webview.variations;
 
-import android.annotation.TargetApi;
 import android.app.Service;
 import android.app.job.JobInfo;
 import android.app.job.JobScheduler;
 import android.content.ComponentName;
 import android.content.Context;
 import android.content.Intent;
-import android.os.Build;
 import android.os.Bundle;
 import android.os.Handler;
 import android.os.IBinder;
@@ -48,7 +46,6 @@
  * checking if there is a current seed fetch job running, if there is pending job and if the seed is
  * expired.
  */
-@TargetApi(Build.VERSION_CODES.LOLLIPOP) // JobService requires API level 21.
 public class AwVariationsConfigurationService extends Service {
     private static final String TAG = "AwVariatnsConfigSvc";
 
diff --git a/android_webview/java/src/org/chromium/android_webview/variations/AwVariationsSeedFetchService.java b/android_webview/java/src/org/chromium/android_webview/variations/AwVariationsSeedFetchService.java
index b836476..f82cd2c 100644
--- a/android_webview/java/src/org/chromium/android_webview/variations/AwVariationsSeedFetchService.java
+++ b/android_webview/java/src/org/chromium/android_webview/variations/AwVariationsSeedFetchService.java
@@ -4,7 +4,6 @@
 
 package org.chromium.android_webview.variations;
 
-import android.annotation.TargetApi;
 import android.app.job.JobParameters;
 import android.app.job.JobService;
 import android.content.ComponentName;
@@ -12,7 +11,6 @@
 import android.content.Intent;
 import android.content.ServiceConnection;
 import android.os.AsyncTask;
-import android.os.Build;
 import android.os.Bundle;
 import android.os.IBinder;
 import android.os.Message;
@@ -37,7 +35,6 @@
  * Variations Seed Fetch Service which is one part of the work of adding Variations to Android
  * WebView.
  */
-@TargetApi(Build.VERSION_CODES.LOLLIPOP) // JobService requires API level 21.
 public class AwVariationsSeedFetchService extends JobService {
     private static final String TAG = "AwVartnsSeedFetchSvc";
 
diff --git a/android_webview/javatests/AndroidManifest.xml b/android_webview/javatests/AndroidManifest.xml
index e369d32..b4f2aa0 100644
--- a/android_webview/javatests/AndroidManifest.xml
+++ b/android_webview/javatests/AndroidManifest.xml
@@ -4,7 +4,7 @@
        in the LICENSE file. -->
   <manifest xmlns:android="http://schemas.android.com/apk/res/android"
       package="org.chromium.android_webview.test">
-    <uses-sdk android:minSdkVersion="19" android:targetSdkVersion="23" />
+    <uses-sdk android:minSdkVersion="21" android:targetSdkVersion="23" />
     <instrumentation android:name="org.chromium.base.test.BaseChromiumAndroidJUnitRunner"
         android:targetPackage="org.chromium.android_webview.shell"
         android:label="Tests for org.chromium.android_webview"
diff --git a/android_webview/javatests/src/org/chromium/android_webview/test/AwContentsClientOnUnhandledKeyEventTest.java b/android_webview/javatests/src/org/chromium/android_webview/test/AwContentsClientOnUnhandledKeyEventTest.java
index b657f68..3560acb9 100644
--- a/android_webview/javatests/src/org/chromium/android_webview/test/AwContentsClientOnUnhandledKeyEventTest.java
+++ b/android_webview/javatests/src/org/chromium/android_webview/test/AwContentsClientOnUnhandledKeyEventTest.java
@@ -4,7 +4,6 @@
 
 package org.chromium.android_webview.test;
 
-import android.os.Build;
 import android.support.test.filters.SmallTest;
 import android.view.KeyEvent;
 
@@ -18,7 +17,6 @@
 import org.chromium.base.test.util.CallbackHelper;
 import org.chromium.base.test.util.DisabledTest;
 import org.chromium.base.test.util.Feature;
-import org.chromium.base.test.util.MinAndroidSdkLevel;
 import org.chromium.content_public.browser.ImeAdapter;
 
 import java.util.ArrayList;
@@ -29,7 +27,6 @@
  * Tests for the WebViewClient.onUnhandledKeyEvent() method.
  */
 @RunWith(AwJUnit4ClassRunner.class)
-@MinAndroidSdkLevel(Build.VERSION_CODES.KITKAT)
 public class AwContentsClientOnUnhandledKeyEventTest {
     @Rule
     public AwActivityTestRule mActivityTestRule = new AwActivityTestRule();
diff --git a/android_webview/javatests/src/org/chromium/android_webview/test/SafeBrowsingTest.java b/android_webview/javatests/src/org/chromium/android_webview/test/SafeBrowsingTest.java
index 54f4e2f..3477a0d7 100644
--- a/android_webview/javatests/src/org/chromium/android_webview/test/SafeBrowsingTest.java
+++ b/android_webview/javatests/src/org/chromium/android_webview/test/SafeBrowsingTest.java
@@ -122,6 +122,7 @@
     // These URLs will be CTS-tested and should not be changed.
     private static final String WEB_UI_MALWARE_URL = "chrome://safe-browsing/match?type=malware";
     private static final String WEB_UI_PHISHING_URL = "chrome://safe-browsing/match?type=phishing";
+    private static final String WEB_UI_HOST = "safe-browsing";
 
     /**
      * A fake SafeBrowsingApiHandler which treats URLs ending in MALWARE_HTML_PATH as malicious URLs
@@ -499,12 +500,7 @@
     public void testSafeBrowsingWhitelistedUnsafePagesDontShowInterstitial() throws Throwable {
         loadGreenPage();
         final String responseUrl = mTestServer.getURL(MALWARE_HTML_PATH);
-        ThreadUtils.runOnUiThreadBlocking(() -> {
-            String host = Uri.parse(responseUrl).getHost();
-            ArrayList<String> s = new ArrayList<String>();
-            s.add(host);
-            AwContentsStatics.setSafeBrowsingWhitelist(s, null);
-        });
+        verifyWhiteListRule(Uri.parse(responseUrl).getHost(), true);
         mActivityTestRule.loadUrlSync(
                 mAwContents, mContentsClient.getOnPageFinishedHelper(), responseUrl);
         assertTargetPageHasLoaded(MALWARE_PAGE_BACKGROUND_COLOR);
@@ -513,6 +509,20 @@
     @Test
     @SmallTest
     @Feature({"AndroidWebView"})
+    public void testSafeBrowsingWhitelistHardcodedWebUiPages() throws Throwable {
+        loadGreenPage();
+        verifyWhiteListRule(WEB_UI_HOST, true);
+        mActivityTestRule.loadUrlSync(
+                mAwContents, mContentsClient.getOnPageFinishedHelper(), WEB_UI_MALWARE_URL);
+        mActivityTestRule.loadUrlSync(
+                mAwContents, mContentsClient.getOnPageFinishedHelper(), WEB_UI_PHISHING_URL);
+
+        // Assume the pages are whitelisted, since we successfully loaded them.
+    }
+
+    @Test
+    @SmallTest
+    @Feature({"AndroidWebView"})
     public void testCallbackCalledOnSafeBrowsingBadWhitelistRule() throws Throwable {
         verifyWhiteListRule("http://www.google.com", false);
     }
diff --git a/android_webview/javatests/src/org/chromium/android_webview/test/variations/AwVariationsConfigurationServiceTest.java b/android_webview/javatests/src/org/chromium/android_webview/test/variations/AwVariationsConfigurationServiceTest.java
index 7c4fb9b9..afbda23 100644
--- a/android_webview/javatests/src/org/chromium/android_webview/test/variations/AwVariationsConfigurationServiceTest.java
+++ b/android_webview/javatests/src/org/chromium/android_webview/test/variations/AwVariationsConfigurationServiceTest.java
@@ -7,8 +7,6 @@
 import static org.chromium.android_webview.variations.AwVariationsUtils.SEED_DATA_FILENAME;
 import static org.chromium.android_webview.variations.AwVariationsUtils.SEED_PREF_FILENAME;
 
-import android.annotation.TargetApi;
-import android.os.Build;
 import android.os.ParcelFileDescriptor;
 import android.support.test.InstrumentationRegistry;
 import android.support.test.filters.MediumTest;
@@ -28,7 +26,6 @@
 import org.chromium.base.ContextUtils;
 import org.chromium.base.PathUtils;
 import org.chromium.base.test.BaseJUnit4ClassRunner;
-import org.chromium.base.test.util.MinAndroidSdkLevel;
 import org.chromium.components.variations.firstrun.VariationsSeedFetcher.SeedInfo;
 
 import java.io.File;
@@ -40,8 +37,6 @@
  * AwVariationsSeedFetchService which can't work under Android L.
  */
 @RunWith(BaseJUnit4ClassRunner.class)
-@MinAndroidSdkLevel(Build.VERSION_CODES.LOLLIPOP)
-@TargetApi(Build.VERSION_CODES.LOLLIPOP) // JobService requires API level 21.
 public class AwVariationsConfigurationServiceTest {
     private SeedInfo mSeedInfo;
 
diff --git a/android_webview/lib/aw_main_delegate.cc b/android_webview/lib/aw_main_delegate.cc
index 8aae749..50d03fa 100644
--- a/android_webview/lib/aw_main_delegate.cc
+++ b/android_webview/lib/aw_main_delegate.cc
@@ -30,6 +30,7 @@
 #include "base/strings/string_number_conversions.h"
 #include "base/threading/thread_restrictions.h"
 #include "cc/base/switches.h"
+#include "components/autofill/core/common/autofill_features.h"
 #include "components/crash/content/app/breakpad_linux.h"
 #include "components/crash/core/common/crash_key.h"
 #include "components/safe_browsing/android/safe_browsing_api_handler_bridge.h"
@@ -158,6 +159,9 @@
 
   CommandLineHelper::AddDisabledFeature(*cl, features::kMojoInputMessages.name);
 
+  CommandLineHelper::AddEnabledFeature(
+      *cl, autofill::features::kAutofillSkipComparingInferredLabels.name);
+
   android_webview::RegisterPathProvider();
 
   safe_browsing_api_handler_.reset(
diff --git a/ash/BUILD.gn b/ash/BUILD.gn
index 8a1cc098..4398ffe 100644
--- a/ash/BUILD.gn
+++ b/ash/BUILD.gn
@@ -1825,6 +1825,8 @@
     "system/power/power_button_screenshot_controller_test_api.h",
     "system/power/power_button_test_base.cc",
     "system/power/power_button_test_base.h",
+    "system/power/power_event_observer_test_api.cc",
+    "system/power/power_event_observer_test_api.h",
     "system/power/tablet_power_button_controller_test_api.cc",
     "system/power/tablet_power_button_controller_test_api.h",
     "system/status_area_widget_test_helper.cc",
diff --git a/ash/README.md b/ash/README.md
index a6dcdb9e..dac7464 100644
--- a/ash/README.md
+++ b/ash/README.md
@@ -28,7 +28,7 @@
 //services/ui. Ash continues to use aura, but aura is backed by mus. Code to
 support mus is found in the ash directory. There should be relatively few
 differences between the pure aura and the aura-mus versions of ash. Ash can by
-run in mus mode by passing the --mus command line flag.
+run in mus mode by passing the --enable-features=Mus command line flag.
 
 Ash is also transitioning to run as a mojo service in its own process. This
 means that code in chrome cannot call into ash directly, but must use the mojo
@@ -36,7 +36,7 @@
 
 Out-of-process Ash is referred to as "mash" (mojo ash). In-process ash is
 referred to as "classic ash". Ash can run in either mode depending on the
---mash command line flag.
+--enable-features=Mash command line flag.
 
 In the few cases where chrome code is allowed to call into ash (e.g. code that
 will only ever run in classic ash) the #include lines have "// mash-ok"
@@ -45,15 +45,16 @@
 
 Mustash Tests
 -----
-ash_unittests --mus runs the test suite in mus mode. ash_unittests --mash runs
-in mash mode. Some tests will fail because the underlying code has not yet been
-ported to work with mash. We use filter files to skip these tests, because it
-makes it easier to run the entire suite without the filter to see what passes.
+ash_unittests --enable-features=Mus runs the test suite in mus mode.
+ash_unittests --enable-features=Mash runs in mash mode. Some tests will fail
+because the underlying code has not yet been ported to work with mash. We use
+filter files to skip these tests, because it makes it easier to run the entire
+suite without the filter to see what passes.
 
 To simulate what the bots run (e.g. to check if you broke an existing test that
 works under mash) you can run:
 
-`ash_unittests --mash --test-launcher-filter-file=testing/buildbot/filters/ash_unittests_mash.filter`
+`ash_unittests --enable-features=Mash --test-launcher-filter-file=testing/buildbot/filters/ash_unittests_mash.filter`
 
 Any new feature you add (and its tests) should work under mash. If your test
 cannot pass under mash due to some dependency being broken you may add the test
diff --git a/ash/default_wallpaper_delegate.cc b/ash/default_wallpaper_delegate.cc
index dcad028..c4ed540 100644
--- a/ash/default_wallpaper_delegate.cc
+++ b/ash/default_wallpaper_delegate.cc
@@ -4,16 +4,8 @@
 
 #include "ash/default_wallpaper_delegate.h"
 
-#include "ash/shell.h"
-#include "ash/wallpaper/wallpaper_controller.h"
-#include "ui/wm/core/window_animations.h"
-
 namespace ash {
 
-int DefaultWallpaperDelegate::GetAnimationType() {
-  return ::wm::WINDOW_VISIBILITY_ANIMATION_TYPE_FADE;
-}
-
 int DefaultWallpaperDelegate::GetAnimationDurationOverride() {
   return 0;
 }
@@ -21,12 +13,4 @@
 void DefaultWallpaperDelegate::SetAnimationDurationOverride(
     int animation_duration_in_ms) {}
 
-bool DefaultWallpaperDelegate::ShouldShowInitialAnimation() {
-  return false;
-}
-
-void DefaultWallpaperDelegate::OnWallpaperAnimationFinished() {}
-
-void DefaultWallpaperDelegate::OnWallpaperBootAnimationFinished() {}
-
 }  // namespace ash
diff --git a/ash/default_wallpaper_delegate.h b/ash/default_wallpaper_delegate.h
index 51786f0..e50b467 100644
--- a/ash/default_wallpaper_delegate.h
+++ b/ash/default_wallpaper_delegate.h
@@ -17,12 +17,8 @@
   ~DefaultWallpaperDelegate() override {}
 
   // WallpaperDelegate overrides:
-  int GetAnimationType() override;
   int GetAnimationDurationOverride() override;
   void SetAnimationDurationOverride(int animation_duration_in_ms) override;
-  bool ShouldShowInitialAnimation() override;
-  void OnWallpaperAnimationFinished() override;
-  void OnWallpaperBootAnimationFinished() override;
 
  private:
   DISALLOW_COPY_AND_ASSIGN(DefaultWallpaperDelegate);
diff --git a/ash/display/display_synchronizer.cc b/ash/display/display_synchronizer.cc
index 952f764..920f2c0e 100644
--- a/ash/display/display_synchronizer.cc
+++ b/ash/display/display_synchronizer.cc
@@ -10,6 +10,7 @@
 #include "services/ui/public/interfaces/window_manager_constants.mojom.h"
 #include "ui/aura/mus/window_manager_delegate.h"
 #include "ui/aura/mus/window_tree_host_mus.h"
+#include "ui/base/ui_base_features.h"
 #include "ui/base/ui_base_switches_util.h"
 #include "ui/display/manager/display_manager.h"
 #include "ui/display/manager/managed_display_info.h"
@@ -67,7 +68,7 @@
       Shell::Get()->window_tree_host_manager()->mirror_window_controller();
   for (const auto& mirror :
        display_manager->software_mirroring_display_list()) {
-    if (::switches::IsMusHostingViz()) {
+    if (base::FeatureList::IsEnabled(features::kMash)) {
       // If mus is hosting viz, the window server handle mirrors internally.
       mirrors.push_back(mirror);
       metrics.push_back(GetMetricsForDisplay(mirror.id()));
diff --git a/ash/display/mirror_window_controller.cc b/ash/display/mirror_window_controller.cc
index 41fe194c..73446af0 100644
--- a/ash/display/mirror_window_controller.cc
+++ b/ash/display/mirror_window_controller.cc
@@ -24,6 +24,7 @@
 #include "ui/aura/window_event_dispatcher.h"
 #include "ui/aura/window_tree_host.h"
 #include "ui/base/layout.h"
+#include "ui/base/ui_base_features.h"
 #include "ui/base/ui_base_switches_util.h"
 #include "ui/compositor/reflector.h"
 #include "ui/display/display_layout.h"
@@ -342,7 +343,7 @@
     aura::WindowTreeHost* host) {
   DCHECK_NE(host->GetAcceleratedWidget(), gfx::kNullAcceleratedWidget);
   DCHECK_NE(Shell::GetAshConfig(), Config::CLASSIC);
-  DCHECK(!switches::IsMusHostingViz());
+  DCHECK(!base::FeatureList::IsEnabled(features::kMash));
   MirroringHostInfo* info = mirroring_host_info_map_[host->GetDisplayId()];
   if (reflector_) {
     reflector_->AddMirroringLayer(info->mirror_window->layer());
diff --git a/ash/display/window_tree_host_manager.cc b/ash/display/window_tree_host_manager.cc
index 0154991b..24a68659 100644
--- a/ash/display/window_tree_host_manager.cc
+++ b/ash/display/window_tree_host_manager.cc
@@ -41,6 +41,7 @@
 #include "ui/base/class_property.h"
 #include "ui/base/ime/input_method_factory.h"
 #include "ui/base/l10n/l10n_util.h"
+#include "ui/base/ui_base_features.h"
 #include "ui/base/ui_base_switches_util.h"
 #include "ui/compositor/compositor.h"
 #include "ui/display/display.h"
@@ -100,7 +101,7 @@
 
 bool ShouldUpdateMirrorWindowController() {
   return aura::Env::GetInstance()->mode() == aura::Env::Mode::LOCAL ||
-         !::switches::IsMusHostingViz();
+         !base::FeatureList::IsEnabled(features::kMash);
 }
 
 }  // namespace
diff --git a/ash/frame/custom_frame_view_ash.cc b/ash/frame/custom_frame_view_ash.cc
index 917189f..c7b434d 100644
--- a/ash/frame/custom_frame_view_ash.cc
+++ b/ash/frame/custom_frame_view_ash.cc
@@ -523,45 +523,24 @@
   return header_view_->GetInactiveFrameColor();
 }
 
-void CustomFrameViewAsh::MaybePaintHeaderForSplitview(
-    SplitViewController::State state) {
-  if (state == SplitViewController::NO_SNAP) {
-    SetShouldPaintHeader(/*paint=*/false);
-    return;
-  }
-
-  SplitViewController* controller = Shell::Get()->split_view_controller();
-  aura::Window* window = nullptr;
-  if (state == SplitViewController::LEFT_SNAPPED)
-    window = controller->left_window();
-  else if (state == SplitViewController::RIGHT_SNAPPED)
-    window = controller->right_window();
-
-  // TODO(sammiequon): This works for now, but we may have to check if
-  // |frame_|'s native window is in the overview list instead.
-  if (window && window == frame_->GetNativeWindow())
-    SetShouldPaintHeader(/*paint=*/true);
-}
-
 void CustomFrameViewAsh::SetShouldPaintHeader(bool paint) {
   header_view_->SetShouldPaintHeader(paint);
 }
 
 void CustomFrameViewAsh::OnOverviewModeStarting() {
   in_overview_mode_ = true;
-  SetShouldPaintHeader(false);
+  OnOverviewOrSplitViewModeChanged();
 }
 
 void CustomFrameViewAsh::OnOverviewModeEnded() {
   in_overview_mode_ = false;
-  SetShouldPaintHeader(true);
+  OnOverviewOrSplitViewModeChanged();
 }
 
 void CustomFrameViewAsh::OnSplitViewStateChanged(
     SplitViewController::State /* previous_state */,
-    SplitViewController::State state) {
-  if (in_overview_mode_)
-    MaybePaintHeaderForSplitview(state);
+    SplitViewController::State /* current_state */) {
+  OnOverviewOrSplitViewModeChanged();
 }
 
 ////////////////////////////////////////////////////////////////////////////////
@@ -597,4 +576,18 @@
   return header_view_->GetPreferredHeight();
 }
 
+void CustomFrameViewAsh::OnOverviewOrSplitViewModeChanged() {
+  SplitViewController* split_view_controller =
+      Shell::Get()->split_view_controller();
+  if (in_overview_mode_ && split_view_controller->IsSplitViewModeActive() &&
+      split_view_controller->GetDefaultSnappedWindow() ==
+          frame_->GetNativeWindow()) {
+    // TODO(sammiequon): This works for now, but we may have to check if
+    // |frame_|'s native window is in the overview list instead.
+    SetShouldPaintHeader(true);
+  } else {
+    SetShouldPaintHeader(!in_overview_mode_);
+  }
+}
+
 }  // namespace ash
diff --git a/ash/frame/custom_frame_view_ash.h b/ash/frame/custom_frame_view_ash.h
index 04a8ee38..4c9b2a7 100644
--- a/ash/frame/custom_frame_view_ash.h
+++ b/ash/frame/custom_frame_view_ash.h
@@ -102,11 +102,6 @@
   void SchedulePaintInRect(const gfx::Rect& r) override;
   void SetVisible(bool visible) override;
 
-  // Called when splitview state changes. Depending on |state| and if the window
-  // associated with |widget_| is the snapped window, paint the header in
-  // overview mode.
-  void MaybePaintHeaderForSplitview(SplitViewController::State state);
-
   // If |paint| is false, we should not paint the header. Used for overview mode
   // with OnOverviewModeStarting() and OnOverviewModeEnded() to hide/show the
   // header of v2 and ARC apps.
@@ -143,6 +138,12 @@
   // Height from top of window to top of client area.
   int NonClientTopBorderHeight() const;
 
+  // Called when overview mode or split view state changed. If overview mode and
+  // split view mode are both active at the same time, the header of the window
+  // in split view should be visible, but the headers of other windows in
+  // overview are not.
+  void OnOverviewOrSplitViewModeChanged();
+
   // Not owned.
   views::Widget* frame_;
 
diff --git a/ash/manifest.json b/ash/manifest.json
index 0abc643..3a33c83 100644
--- a/ash/manifest.json
+++ b/ash/manifest.json
@@ -26,6 +26,7 @@
           "ash::mojom::SessionController",
           "ash::mojom::ShelfController",
           "ash::mojom::ShutdownController",
+          "ash::mojom::SplitViewController",
           "ash::mojom::SystemTray",
           "ash::mojom::TabletModeController",
           "ash::mojom::TrayAction",
diff --git a/ash/mojo_interface_factory.cc b/ash/mojo_interface_factory.cc
index f63a1aa..121d3ce3 100644
--- a/ash/mojo_interface_factory.cc
+++ b/ash/mojo_interface_factory.cc
@@ -32,6 +32,7 @@
 #include "ash/tray_action/tray_action.h"
 #include "ash/voice_interaction/voice_interaction_controller.h"
 #include "ash/wallpaper/wallpaper_controller.h"
+#include "ash/wm/splitview/split_view_controller.h"
 #include "ash/wm/tablet_mode/tablet_mode_controller.h"
 #include "base/bind.h"
 #include "base/lazy_instance.h"
@@ -164,6 +165,11 @@
   Shell::Get()->wallpaper_controller()->BindRequest(std::move(request));
 }
 
+void BindSplitViewRequestOnMainThread(
+    mojom::SplitViewControllerRequest request) {
+  Shell::Get()->split_view_controller()->BindRequest(std::move(request));
+}
+
 }  // namespace
 
 void RegisterInterfaces(
@@ -235,6 +241,8 @@
                          main_thread_task_runner);
   registry->AddInterface(base::Bind(&BindWallpaperRequestOnMainThread),
                          main_thread_task_runner);
+  registry->AddInterface(base::Bind(&BindSplitViewRequestOnMainThread),
+                         main_thread_task_runner);
 
   // Inject additional optional interfaces.
   if (g_register_interfaces_callback.Get()) {
diff --git a/ash/public/interfaces/BUILD.gn b/ash/public/interfaces/BUILD.gn
index ca673b93..4c1c82a 100644
--- a/ash/public/interfaces/BUILD.gn
+++ b/ash/public/interfaces/BUILD.gn
@@ -37,6 +37,7 @@
     "session_controller.mojom",
     "shelf.mojom",
     "shutdown.mojom",
+    "split_view.mojom",
     "system_tray.mojom",
     "tablet_mode.mojom",
     "tray_action.mojom",
diff --git a/ash/public/interfaces/split_view.mojom b/ash/public/interfaces/split_view.mojom
new file mode 100644
index 0000000..ee5581e
--- /dev/null
+++ b/ash/public/interfaces/split_view.mojom
@@ -0,0 +1,24 @@
+// 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.
+
+module ash.mojom;
+
+enum SplitViewState {
+  NO_SNAP,
+  LEFT_SNAPPED,
+  RIGHT_SNAPPED,
+  BOTH_SNAPPED,
+};
+
+// Used to listen for split view state changes.
+interface SplitViewObserver {
+  OnSplitViewStateChanged(SplitViewState current_state);
+};
+
+// The split view controller that allows clients (Chrome) to observe the split
+// view state changes.
+interface SplitViewController {
+  // Adds an observer.
+  AddObserver(SplitViewObserver observer);
+};
diff --git a/ash/public/interfaces/wallpaper.mojom b/ash/public/interfaces/wallpaper.mojom
index 574669c..ea6cf2ff 100644
--- a/ash/public/interfaces/wallpaper.mojom
+++ b/ash/public/interfaces/wallpaper.mojom
@@ -186,6 +186,12 @@
   // Signals to the client that ash is ready to set wallpapers. The client is
   // able to decide whatever the first wallpaper it wants to display.
   OnReadyToSetWallpaper();
+
+  // TODO(crbug.com/784495, 776464): Consider removing this after views-based
+  // login is enabled.
+  // Notifies the client that the animation of the first wallpaper since
+  // the controller initialization has completed.
+  OnFirstWallpaperAnimationFinished();
 };
 
 // Used to listen for wallpaper state changed.
diff --git a/ash/root_window_controller.cc b/ash/root_window_controller.cc
index 196c328..5dfe0fd7 100644
--- a/ash/root_window_controller.cc
+++ b/ash/root_window_controller.cc
@@ -37,7 +37,6 @@
 #include "ash/touch/touch_hud_projection.h"
 #include "ash/touch/touch_observer_hud.h"
 #include "ash/virtual_keyboard_container_layout_manager.h"
-#include "ash/wallpaper/wallpaper_delegate.h"
 #include "ash/wallpaper/wallpaper_widget_controller.h"
 #include "ash/wm/always_on_top_controller.h"
 #include "ash/wm/container_finder.h"
diff --git a/ash/shelf/shelf_context_menu_model_unittest.cc b/ash/shelf/shelf_context_menu_model_unittest.cc
index 44f4776..c8fbead8 100644
--- a/ash/shelf/shelf_context_menu_model_unittest.cc
+++ b/ash/shelf/shelf_context_menu_model_unittest.cc
@@ -58,6 +58,7 @@
   // mojom::WallpaperControllerClient:
   void OpenWallpaperPicker() override { open_count_++; }
   void OnReadyToSetWallpaper() override {}
+  void OnFirstWallpaperAnimationFinished() override {}
 
  private:
   size_t open_count_ = 0;
diff --git a/ash/shell.cc b/ash/shell.cc
index bdc043e..a3e18f3 100644
--- a/ash/shell.cc
+++ b/ash/shell.cc
@@ -1347,7 +1347,8 @@
     std::unique_ptr<::PrefService> pref_service) {
   DCHECK(!local_state_);
   // |pref_service| is null if can't connect to Chrome (as happens when
-  // running mash outside of chrome --mash and chrome isn't built).
+  // running mash outside of chrome --enable-features=Mash and chrome isn't
+  // built).
   if (!pref_service)
     return;
 
diff --git a/ash/shell_port_mus.cc b/ash/shell_port_mus.cc
index f598e0a..4097196 100644
--- a/ash/shell_port_mus.cc
+++ b/ash/shell_port_mus.cc
@@ -42,6 +42,7 @@
 #include "ui/aura/mus/window_tree_host_mus.h"
 #include "ui/aura/mus/window_tree_host_mus_init_params.h"
 #include "ui/aura/window.h"
+#include "ui/base/ui_base_features.h"
 #include "ui/base/ui_base_switches_util.h"
 #include "ui/display/manager/display_manager.h"
 #include "ui/display/manager/forwarding_display_delegate.h"
@@ -210,9 +211,9 @@
   aura_init_params.display_init_params = std::move(display_params);
   aura_init_params.use_classic_ime = !Shell::ShouldUseIMEService();
   aura_init_params.uses_real_accelerated_widget =
-      !::switches::IsMusHostingViz();
+      !base::FeatureList::IsEnabled(features::kMash);
 
-  if (!::switches::IsMusHostingViz()) {
+  if (!base::FeatureList::IsEnabled(features::kMash)) {
     if (init_params.mirroring_unified) {
       return std::make_unique<AshWindowTreeHostMusMirroringUnified>(
           std::move(aura_init_params), init_params.display_id,
@@ -278,7 +279,7 @@
 
 void ShellPortMus::AddVideoDetectorObserver(
     viz::mojom::VideoDetectorObserverPtr observer) {
-  if (switches::IsMusHostingViz()) {
+  if (base::FeatureList::IsEnabled(features::kMash)) {
     // We may not have access to the connector in unit tests.
     if (!window_manager_->connector())
       return;
diff --git a/ash/system/flag_warning/flag_warning_tray.cc b/ash/system/flag_warning/flag_warning_tray.cc
index a7c003e5..065812a 100644
--- a/ash/system/flag_warning/flag_warning_tray.cc
+++ b/ash/system/flag_warning/flag_warning_tray.cc
@@ -25,7 +25,7 @@
 namespace ash {
 namespace {
 
-const char kTooltipText[] = "Running with flag --mash";
+const char kTooltipText[] = "Running with feature mash";
 
 }  // namespace
 
@@ -33,7 +33,6 @@
   DCHECK(shelf_);
   SetLayoutManager(std::make_unique<views::FillLayout>());
 
-  // The flag --mash is a chrome-level switch, so check the config instead.
   const bool is_mash = Shell::GetAshConfig() == Config::MASH;
   if (is_mash) {
     container_ = new TrayContainer(shelf);
diff --git a/ash/system/flag_warning/flag_warning_tray.h b/ash/system/flag_warning/flag_warning_tray.h
index 85dea7a..2c04fc82 100644
--- a/ash/system/flag_warning/flag_warning_tray.h
+++ b/ash/system/flag_warning/flag_warning_tray.h
@@ -20,10 +20,10 @@
 class Shelf;
 class TrayContainer;
 
-// Adds an indicator to the status area if certain high-risk flags are enabled,
-// for example --mash. Clicking the button opens about:flags so the user can
-// reset the flag. For consistency with other status area tray views, this view
-// is always created but only made visible when the flag is set.
+// Adds an indicator to the status area if certain high-risk flags or features
+// are enabled, for example mash. Clicking the button opens about:flags so the
+// user can reset the flag. For consistency with other status area tray views,
+// this view is always created but only made visible when the flag is set.
 class ASH_EXPORT FlagWarningTray : public views::View,
                                    public views::ButtonListener {
  public:
diff --git a/ash/system/flag_warning/flag_warning_tray_unittest.cc b/ash/system/flag_warning/flag_warning_tray_unittest.cc
index c1cb7f4..3ea51e6 100644
--- a/ash/system/flag_warning/flag_warning_tray_unittest.cc
+++ b/ash/system/flag_warning/flag_warning_tray_unittest.cc
@@ -22,10 +22,10 @@
                               ->flag_warning_tray_for_testing();
   ASSERT_TRUE(tray);
 
-  // Warning should be visible in ash_unittests --mash, but not in regular
-  // ash_unittests. The warning does not show for Config::MUS because mus will
-  // roll out via experiment/Finch trial and showing the tray would reveal
-  // the experiment state to users.
+  // Warning should be visible in ash_unittests when mash is enabled, but not in
+  // regular ash_unittests. The warning does not show for Config::MUS because
+  // mus will roll out via experiment/Finch trial and showing the tray would
+  // reveal the experiment state to users.
   const bool is_mash = Shell::GetAshConfig() == Config::MASH;
   EXPECT_EQ(tray->visible(), is_mash);
 }
diff --git a/ash/system/power/peripheral_battery_notifier_unittest.cc b/ash/system/power/peripheral_battery_notifier_unittest.cc
index b12cca83..d013087 100644
--- a/ash/system/power/peripheral_battery_notifier_unittest.cc
+++ b/ash/system/power/peripheral_battery_notifier_unittest.cc
@@ -167,7 +167,7 @@
   EXPECT_TRUE(non_bluetooth_device_info.bluetooth_address.empty());
 }
 
-// TODO(crbug.com/765794): Flaky on ash_unittests --mus.
+// TODO(crbug.com/765794): Flaky on ash_unittests with mus.
 TEST_F(PeripheralBatteryNotifierTest, DISABLED_DeviceRemove) {
   message_center::MessageCenter* message_center =
       message_center::MessageCenter::Get();
@@ -183,7 +183,7 @@
               nullptr);
 }
 
-// TODO(crbug.com/765794): Flaky on ash_unittests --mus.
+// TODO(crbug.com/765794): Flaky on ash_unittests with mus.
 TEST_F(PeripheralBatteryNotifierTest, DISABLED_StylusNotification) {
   const std::string kTestStylusBatteryPath =
       "/sys/class/power_supply/hid-AAAA:BBBB:CCCC.DDDD-battery";
diff --git a/ash/system/power/power_event_observer.cc b/ash/system/power/power_event_observer.cc
index a31dd07..83904f77 100644
--- a/ash/system/power/power_event_observer.cc
+++ b/ash/system/power/power_event_observer.cc
@@ -4,50 +4,190 @@
 
 #include "ash/system/power/power_event_observer.h"
 
+#include <map>
+#include <utility>
+
 #include "ash/public/cpp/config.h"
 #include "ash/session/session_controller.h"
 #include "ash/shell.h"
 #include "ash/system/tray/system_tray_notifier.h"
 #include "ash/wm/lock_state_controller.h"
+#include "ash/wm/lock_state_observer.h"
+#include "base/bind.h"
 #include "base/location.h"
+#include "base/scoped_observer.h"
+#include "base/threading/thread_task_runner_handle.h"
 #include "chromeos/dbus/dbus_thread_manager.h"
 #include "ui/aura/window.h"
 #include "ui/aura/window_tree_host.h"
 #include "ui/base/user_activity/user_activity_detector.h"
 #include "ui/compositor/compositor.h"
+#include "ui/compositor/compositor_observer.h"
 #include "ui/display/manager/chromeos/display_configurator.h"
 
 namespace ash {
 
 namespace {
 
-// Tells the compositor for each of the displays to finish all pending
-// rendering requests and block any new ones.
-void StopRenderingRequests() {
-  for (aura::Window* window : Shell::GetAllRootWindows()) {
-    ui::Compositor* compositor = window->GetHost()->compositor();
-    compositor->SetVisible(false);
-  }
-}
-
-// Tells the compositor for each of the displays to resume sending rendering
-// requests to the GPU.
-void ResumeRenderingRequests() {
-  for (aura::Window* window : Shell::GetAllRootWindows())
-    window->GetHost()->compositor()->SetVisible(true);
-}
-
-void OnSuspendDisplaysCompleted(const base::Closure& suspend_callback,
+void OnSuspendDisplaysCompleted(base::OnceClosure suspend_callback,
                                 bool status) {
-  suspend_callback.Run();
+  std::move(suspend_callback).Run();
 }
 
+// Returns whether the screen should be locked when device is suspended.
+bool ShouldLockOnSuspend() {
+  SessionController* controller = ash::Shell::Get()->session_controller();
+
+  return controller->ShouldLockScreenAutomatically() &&
+         controller->CanLockScreen();
+}
+
+// One-shot class that runs a callback after all compositors start and
+// complete two compositing cycles. This should ensure that buffer swap with the
+// current UI has happened.
+// After the first compositing cycle, the display compositor starts drawing the
+// UI changes, and schedules a buffer swap. Given that the display compositor
+// will not start drawing the next frame before the previous swap happens, when
+// the second compositing cycle ends, it should be safe to assume the required
+// buffer swap happened at that point.
+class CompositorWatcher : public ui::CompositorObserver {
+ public:
+  // |callback| - called when all visible root window compositors complete
+  //     required number of compositing cycles. It will not be called after
+  //     CompositorWatcher instance is deleted, nor from the CompositorWatcher
+  //     destructor.
+  explicit CompositorWatcher(base::OnceClosure callback)
+      : callback_(std::move(callback)),
+        compositor_observer_(this),
+        weak_ptr_factory_(this) {
+    Start();
+  }
+  ~CompositorWatcher() override = default;
+
+  // ui::CompositorObserver:
+  void OnCompositingDidCommit(ui::Compositor* compositor) override {
+    if (!pending_compositing_.count(compositor) ||
+        pending_compositing_[compositor].state !=
+            CompositingState::kWaitingForCommit) {
+      return;
+    }
+    pending_compositing_[compositor].state =
+        CompositingState::kWaitingForStarted;
+  }
+  void OnCompositingStarted(ui::Compositor* compositor,
+                            base::TimeTicks start_time) override {
+    if (!pending_compositing_.count(compositor) ||
+        pending_compositing_[compositor].state !=
+            CompositingState::kWaitingForStarted) {
+      return;
+    }
+    pending_compositing_[compositor].state = CompositingState::kWaitingForEnded;
+  }
+  void OnCompositingEnded(ui::Compositor* compositor) override {
+    if (!pending_compositing_.count(compositor))
+      return;
+    CompositorInfo& compositor_info = pending_compositing_[compositor];
+    if (compositor_info.state != CompositingState::kWaitingForEnded)
+      return;
+
+    compositor_info.observed_cycles++;
+    if (compositor_info.observed_cycles < kRequiredCompositingCycles) {
+      compositor_info.state = CompositingState::kWaitingForCommit;
+      compositor->ScheduleDraw();
+      return;
+    }
+
+    compositor_observer_.Remove(compositor);
+    pending_compositing_.erase(compositor);
+
+    RunCallbackIfAllCompositingEnded();
+  }
+  void OnCompositingLockStateChanged(ui::Compositor* compositor) override {}
+  void OnCompositingChildResizing(ui::Compositor* compositor) override {}
+  void OnCompositingShuttingDown(ui::Compositor* compositor) override {
+    compositor_observer_.Remove(compositor);
+    pending_compositing_.erase(compositor);
+
+    RunCallbackIfAllCompositingEnded();
+  }
+
+ private:
+  // CompositorWatcher observes compositors for compositing events, in order to
+  // determine whether compositing cycles end for all root window compositors.
+  // This enum is used to track this cycle. Compositing goes through the
+  // following states: DidCommit -> CompositingStarted -> CompositingEnded.
+  enum class CompositingState {
+    kWaitingForCommit,
+    kWaitingForStarted,
+    kWaitingForEnded,
+  };
+
+  struct CompositorInfo {
+    // State of the current compositing cycle.
+    CompositingState state = CompositingState::kWaitingForCommit;
+
+    // Number of observed compositing cycles.
+    int observed_cycles = 0;
+  };
+
+  // Number of compositing cycles that have to complete for each compositor
+  // in order for a CompositorWatcher to run the callback.
+  static constexpr int kRequiredCompositingCycles = 2;
+
+  // Starts observing all visible root window compositors.
+  void Start() {
+    for (aura::Window* window : Shell::GetAllRootWindows()) {
+      ui::Compositor* compositor = window->GetHost()->compositor();
+      if (!compositor->IsVisible())
+        continue;
+
+      DCHECK(!pending_compositing_.count(compositor));
+      pending_compositing_[compositor].state =
+          CompositingState::kWaitingForCommit;
+      compositor_observer_.Add(compositor);
+
+      // Schedule a draw to force at least one more compositing cycle.
+      compositor->ScheduleDraw();
+    }
+
+    // Post task to make sure callback is not invoked synchronously as watcher
+    // is started.
+    base::ThreadTaskRunnerHandle::Get()->PostTask(
+        FROM_HERE,
+        base::BindOnce(&CompositorWatcher::RunCallbackIfAllCompositingEnded,
+                       weak_ptr_factory_.GetWeakPtr()));
+  }
+
+  // If all observed root window compositors have gone through a compositing
+  // cycle, runs |callback_|.
+  void RunCallbackIfAllCompositingEnded() {
+    if (pending_compositing_.empty() && callback_)
+      std::move(callback_).Run();
+  }
+
+  base::OnceClosure callback_;
+
+  // Per-compositor compositing state tracked by |this|. The map will
+  // not contain compositors that were not visible at the time the
+  // CompositorWatcher was started - the main purpose of tracking compositing
+  // state is to determine whether compositors can be safely stopped (i.e. their
+  // visibility set to false), so there should be no need for tracking
+  // compositors that were hidden to start with.
+  std::map<ui::Compositor*, CompositorInfo> pending_compositing_;
+  ScopedObserver<ui::Compositor, ui::CompositorObserver> compositor_observer_;
+
+  base::WeakPtrFactory<CompositorWatcher> weak_ptr_factory_;
+
+  DISALLOW_COPY_AND_ASSIGN(CompositorWatcher);
+};
+
 }  // namespace
 
 PowerEventObserver::PowerEventObserver()
-    : session_observer_(this),
-      screen_locked_(Shell::Get()->session_controller()->IsScreenLocked()),
-      waiting_for_lock_screen_animations_(false) {
+    : lock_state_(Shell::Get()->session_controller()->IsScreenLocked()
+                      ? LockState::kLocked
+                      : LockState::kUnlocked),
+      session_observer_(this) {
   chromeos::DBusThreadManager::Get()->GetPowerManagerClient()->AddObserver(
       this);
 }
@@ -59,74 +199,47 @@
 
 void PowerEventObserver::OnLockAnimationsComplete() {
   VLOG(1) << "Screen locker animations have completed.";
-  waiting_for_lock_screen_animations_ = false;
+  if (lock_state_ != LockState::kLocking)
+    return;
 
-  if (!screen_lock_callback_.is_null()) {
-    StopRenderingRequests();
+  lock_state_ = LockState::kLockedCompositingPending;
 
-    screen_lock_callback_.Run();
-    screen_lock_callback_.Reset();
-  }
+  // The |compositor_watcher_| is owned by this, and the callback passed to it
+  // won't be called  after |compositor_watcher_|'s destruction, so
+  // base::Unretained is safe here.
+  compositor_watcher_ = std::make_unique<CompositorWatcher>(
+      base::BindOnce(&PowerEventObserver::OnCompositorsReadyForSuspend,
+                     base::Unretained(this)));
 }
 
 void PowerEventObserver::SuspendImminent(
     power_manager::SuspendImminent::Reason reason) {
-  SessionController* controller = Shell::Get()->session_controller();
+  suspend_in_progress_ = true;
 
-  // This class is responsible for disabling all rendering requests at suspend
-  // time and then enabling them at resume time.  When the
-  // auto-screen-lock pref is not set this is easy to do since
-  // StopRenderingRequests() is just called directly from this function.  If the
-  // auto-screen-lock pref _is_ set, then the suspend needs to be delayed
-  // until the lock screen is fully visible.  While it is sufficient from a
-  // security perspective to block only until the lock screen is ready, which
-  // guarantees that the contents of the user's screen are no longer visible,
-  // this leads to poor UX on the first resume since neither the user pod nor
-  // the header bar will be visible for a few hundred milliseconds until the GPU
-  // process starts rendering again.  To deal with this, the suspend is delayed
-  // until all the lock screen animations have completed and the suspend request
-  // is unblocked from OnLockAnimationsComplete().
-  if (!screen_locked_ && controller->ShouldLockScreenAutomatically() &&
-      controller->CanLockScreen()) {
-    screen_lock_callback_ = chromeos::DBusThreadManager::Get()
-                                ->GetPowerManagerClient()
-                                ->GetSuspendReadinessCallback(FROM_HERE);
-    VLOG(1) << "Requesting screen lock from PowerEventObserver";
-    // TODO(warx): once crbug.com/748732 is fixed, we probably can treat
-    // auto-screen-lock pref set and not set cases as the same. Also remove
-    // |waiting_for_lock_screen_animations_|.
-    Shell::Get()->lock_state_controller()->LockWithoutAnimation();
-  } else if (waiting_for_lock_screen_animations_) {
-    // The auto-screen-lock pref has been set and the lock screen is ready
-    // but the animations have not completed yet.  This can happen if a suspend
-    // request is canceled after the lock screen is ready but before the
-    // animations have completed and then another suspend request is immediately
-    // started.  In practice, it is highly unlikely that this will ever happen
-    // but it's better to be safe since the cost of not dealing with it properly
-    // is a memory leak in the GPU and weird artifacts on the screen.
-    screen_lock_callback_ = chromeos::DBusThreadManager::Get()
-                                ->GetPowerManagerClient()
-                                ->GetSuspendReadinessCallback(FROM_HERE);
+  displays_suspended_callback_ = chromeos::DBusThreadManager::Get()
+                                     ->GetPowerManagerClient()
+                                     ->GetSuspendReadinessCallback(FROM_HERE);
+
+  // Stop compositing immediately if
+  // * the screen lock flow has already completed
+  // * screen is not locked, and should remain unlocked during suspend
+  if (lock_state_ == LockState::kLocked ||
+      (lock_state_ == LockState::kUnlocked && !ShouldLockOnSuspend())) {
+    StopCompositingAndSuspendDisplays();
   } else {
-    // The auto-screen-lock pref is not set or the screen has already been
-    // locked and the animations have completed.  Rendering can be stopped now.
-    StopRenderingRequests();
-  }
-
-  ui::UserActivityDetector::Get()->OnDisplayPowerChanging();
-
-  // TODO(derat): After mus exposes a method for suspending displays, call it
-  // here: http://crbug.com/692193
-  if (Shell::GetAshConfig() != Config::MASH) {
-    Shell::Get()->display_configurator()->SuspendDisplays(
-        base::Bind(&OnSuspendDisplaysCompleted,
-                   chromeos::DBusThreadManager::Get()
-                       ->GetPowerManagerClient()
-                       ->GetSuspendReadinessCallback(FROM_HERE)));
+    // If screen is getting locked during suspend, delay suspend until screen
+    // lock finishes, and post-lock frames get picked up by display compositors.
+    if (lock_state_ == LockState::kUnlocked) {
+      VLOG(1) << "Requesting screen lock from PowerEventObserver";
+      lock_state_ = LockState::kLocking;
+      Shell::Get()->lock_state_controller()->LockWithoutAnimation();
+    }
   }
 }
 
 void PowerEventObserver::SuspendDone(const base::TimeDelta& sleep_duration) {
+  suspend_in_progress_ = false;
+
   // TODO(derat): After mus exposes a method for resuming displays, call it
   // here: http://crbug.com/692193
   if (Shell::GetAshConfig() != Config::MASH)
@@ -137,26 +250,78 @@
   // animation to complete, clear the blocker since the suspend has already
   // completed.  This prevents rendering requests from being blocked after a
   // resume if the lock screen took too long to show.
-  screen_lock_callback_.Reset();
+  displays_suspended_callback_.Reset();
 
-  ResumeRenderingRequests();
+  StartRootWindowCompositors();
 }
 
 void PowerEventObserver::OnLockStateChanged(bool locked) {
   if (locked) {
-    screen_locked_ = true;
-    waiting_for_lock_screen_animations_ = true;
+    lock_state_ = LockState::kLocking;
 
     // The screen is now locked but the pending suspend, if any, will be blocked
     // until all the animations have completed.
-    if (!screen_lock_callback_.is_null()) {
+    if (displays_suspended_callback_) {
       VLOG(1) << "Screen locked due to suspend";
     } else {
       VLOG(1) << "Screen locked without suspend";
     }
   } else {
-    screen_locked_ = false;
+    lock_state_ = LockState::kUnlocked;
+    compositor_watcher_.reset();
+
+    if (suspend_in_progress_) {
+      LOG(WARNING) << "Screen unlocked during suspend";
+      // If screen gets unlocked during suspend, which could theoretically
+      // happen if the user initiated unlock just as device started unlocking
+      // (though, it seems unlikely this would be encountered in practice),
+      // relock the device if required. Otherwise, if suspend is blocked due to
+      // screen locking, unblock it.
+      if (ShouldLockOnSuspend()) {
+        lock_state_ = LockState::kLocking;
+        Shell::Get()->lock_state_controller()->LockWithoutAnimation();
+      } else if (displays_suspended_callback_) {
+        StopCompositingAndSuspendDisplays();
+      }
+    }
   }
 }
 
+void PowerEventObserver::StartRootWindowCompositors() {
+  for (aura::Window* window : Shell::GetAllRootWindows()) {
+    ui::Compositor* compositor = window->GetHost()->compositor();
+    if (!compositor->IsVisible())
+      compositor->SetVisible(true);
+  }
+}
+
+void PowerEventObserver::StopCompositingAndSuspendDisplays() {
+  DCHECK(displays_suspended_callback_);
+  DCHECK(!compositor_watcher_.get());
+  for (aura::Window* window : Shell::GetAllRootWindows()) {
+    ui::Compositor* compositor = window->GetHost()->compositor();
+    compositor->SetVisible(false);
+  }
+
+  ui::UserActivityDetector::Get()->OnDisplayPowerChanging();
+
+  // TODO(derat): After mus exposes a method for suspending displays, call it
+  // here: http://crbug.com/692193
+  if (Shell::GetAshConfig() != Config::MASH) {
+    Shell::Get()->display_configurator()->SuspendDisplays(
+        base::Bind(&OnSuspendDisplaysCompleted,
+                   base::Passed(&displays_suspended_callback_)));
+  } else {
+    std::move(displays_suspended_callback_).Run();
+  }
+}
+
+void PowerEventObserver::OnCompositorsReadyForSuspend() {
+  compositor_watcher_.reset();
+  lock_state_ = LockState::kLocked;
+
+  if (displays_suspended_callback_)
+    StopCompositingAndSuspendDisplays();
+}
+
 }  // namespace ash
diff --git a/ash/system/power/power_event_observer.h b/ash/system/power/power_event_observer.h
index 4d05543..fbf21bb 100644
--- a/ash/system/power/power_event_observer.h
+++ b/ash/system/power/power_event_observer.h
@@ -5,6 +5,8 @@
 #ifndef ASH_SYSTEM_POWER_POWER_EVENT_OBSERVER_H_
 #define ASH_SYSTEM_POWER_POWER_EVENT_OBSERVER_H_
 
+#include <memory>
+
 #include "ash/ash_export.h"
 #include "ash/session/session_observer.h"
 #include "base/callback.h"
@@ -12,9 +14,26 @@
 #include "base/macros.h"
 #include "chromeos/dbus/power_manager_client.h"
 
+namespace ui {
+class CompositorObserver;
+}
+
 namespace ash {
 
-// A class that observes power-management-related events.
+// A class that observes power-management-related events - in particular, it
+// observes the device suspend state and updates display states accordingly.
+// When the device suspends, it suspends all displays and stops compositing.
+// On resume, displays are resumed, and compositing is started again.
+// During suspend, it ensures compositing is not stopped prematurely if the
+// screen is being locked during suspend - display compositing will not be
+// stopped before:
+//  1. lock screen window is shown
+//  2. the compositor goes through at least two compositing cycles after the
+//     screen lock
+// This is done to ensure that displays have picked up frames from after the
+// screen was locked. Without this, displays might initially show
+// pre-screen-lock frames when resumed.
+// For example, see https://crbug.com/807511.
 class ASH_EXPORT PowerEventObserver
     : public chromeos::PowerManagerClient::Observer,
       public SessionObserver {
@@ -36,22 +55,61 @@
   // SessionObserver overrides:
   void OnLockStateChanged(bool locked) override;
 
+ private:
+  friend class PowerEventObserverTestApi;
+
+  enum class LockState {
+    // Screen lock has not been requested, nor detected.
+    kUnlocked,
+    // Screen lock has been requested, or detected, but screen lock has not
+    // reported that it finished showing.
+    kLocking,
+    // Screen has been locked, but all compositors might not have yet picked up
+    // locked screen state - |compositor_watcher_| is observing compositors,
+    // waiting for them to become ready to suspend.
+    kLockedCompositingPending,
+    // Screen is locked, and displays have picked up lock screen changes - it
+    // should be safe to stop compositing and start suspend at this time.
+    kLocked,
+  };
+
+  // Sets all root window compositors' visibility to true.
+  void StartRootWindowCompositors();
+
+  // Sets all root window compositors' visibility to false, and then suspends
+  // displays. It will run |display_suspended_callback_| once displays are
+  // suspended.
+  // This should only be called when it's safe to stop compositing -
+  // either if the screen is not expected to get locked, or all compositors
+  // have gone through compositing cycle after the screen was locked.
+  void StopCompositingAndSuspendDisplays();
+
+  // Callback run by |compositor_watcher_| when it detects that composting
+  // can be stopped for all root windows when device suspends.
+  void OnCompositorsReadyForSuspend();
+
+  LockState lock_state_ = LockState::kUnlocked;
+
   ScopedSessionObserver session_observer_;
 
-  // Is the screen currently locked?
-  bool screen_locked_;
+  // Whether the device is suspending.
+  bool suspend_in_progress_ = false;
 
-  // Have the lock screen animations completed?
-  bool waiting_for_lock_screen_animations_;
+  // Used to observe compositing state after screen lock to detect when display
+  // compositors are in state in which it's safe to proceed with suspend.
+  std::unique_ptr<ui::CompositorObserver> compositor_watcher_;
 
-  // If set, called when the lock screen animations have completed to confirm
-  // that the system is ready to be suspended.
-  base::Closure screen_lock_callback_;
+  // Callback set when device suspend is delayed due to a screen lock - suspend
+  // should be continued when the screen lock finishes showing and display
+  // compositors pick up screen lock changes. All compositors should be stopped
+  // prior to calling this - call StopCompositingAndSuspendDisplays() instead of
+  // runnig this callback directly.
+  // This will only be set while the device is suspending.
+  base::OnceClosure displays_suspended_callback_;
 
- private:
   DISALLOW_COPY_AND_ASSIGN(PowerEventObserver);
 };
 
-}  // namespace chromeos
+}  // namespace ash
 
 #endif  // ASH_SYSTEM_POWER_POWER_EVENT_OBSERVER_H_
diff --git a/ash/system/power/power_event_observer_test_api.cc b/ash/system/power/power_event_observer_test_api.cc
new file mode 100644
index 0000000..44ae12e
--- /dev/null
+++ b/ash/system/power/power_event_observer_test_api.cc
@@ -0,0 +1,57 @@
+// 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 "ash/system/power/power_event_observer_test_api.h"
+
+#include "ash/system/power/power_event_observer.h"
+#include "base/time/time.h"
+#include "ui/compositor/compositor_observer.h"
+
+namespace ash {
+
+PowerEventObserverTestApi::PowerEventObserverTestApi(
+    PowerEventObserver* power_event_observer)
+    : power_event_observer_(power_event_observer) {}
+
+PowerEventObserverTestApi::~PowerEventObserverTestApi() = default;
+
+void PowerEventObserverTestApi::CompositingDidCommit(
+    ui::Compositor* compositor) {
+  if (!power_event_observer_->compositor_watcher_.get())
+    return;
+  power_event_observer_->compositor_watcher_->OnCompositingDidCommit(
+      compositor);
+}
+
+void PowerEventObserverTestApi::CompositingStarted(ui::Compositor* compositor) {
+  if (!power_event_observer_->compositor_watcher_.get())
+    return;
+  power_event_observer_->compositor_watcher_->OnCompositingStarted(
+      compositor, base::TimeTicks());
+}
+
+void PowerEventObserverTestApi::CompositingEnded(ui::Compositor* compositor) {
+  if (!power_event_observer_->compositor_watcher_.get())
+    return;
+  power_event_observer_->compositor_watcher_->OnCompositingEnded(compositor);
+}
+
+void PowerEventObserverTestApi::CompositeFrame(ui::Compositor* compositor) {
+  if (!power_event_observer_->compositor_watcher_.get())
+    return;
+  power_event_observer_->compositor_watcher_->OnCompositingDidCommit(
+      compositor);
+  power_event_observer_->compositor_watcher_->OnCompositingStarted(
+      compositor, base::TimeTicks());
+  power_event_observer_->compositor_watcher_->OnCompositingEnded(compositor);
+}
+
+bool PowerEventObserverTestApi::SimulateCompositorsReadyForSuspend() {
+  if (!power_event_observer_->compositor_watcher_.get())
+    return false;
+  power_event_observer_->OnCompositorsReadyForSuspend();
+  return true;
+}
+
+}  // namespace ash
diff --git a/ash/system/power/power_event_observer_test_api.h b/ash/system/power/power_event_observer_test_api.h
new file mode 100644
index 0000000..dac9b70d
--- /dev/null
+++ b/ash/system/power/power_event_observer_test_api.h
@@ -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.
+
+#ifndef ASH_SYSTEM_POWER_POWER_EVENT_OBSERVER_TEST_API_H_
+#define ASH_SYSTEM_POWER_POWER_EVENT_OBSERVER_TEST_API_H_
+
+#include "base/macros.h"
+
+namespace ui {
+class Compositor;
+}
+
+namespace ash {
+
+class PowerEventObserver;
+
+class PowerEventObserverTestApi {
+ public:
+  explicit PowerEventObserverTestApi(PowerEventObserver* power_event_observer);
+  ~PowerEventObserverTestApi();
+
+  void CompositingDidCommit(ui::Compositor* compositor);
+  void CompositingStarted(ui::Compositor* compositor);
+  void CompositingEnded(ui::Compositor* compositor);
+
+  // Same as calling CompositingDidCommit, CompositingStarted and
+  // CompositingEnded in sequence.
+  void CompositeFrame(ui::Compositor* compositor);
+
+  bool SimulateCompositorsReadyForSuspend();
+
+ private:
+  PowerEventObserver* power_event_observer_;
+
+  DISALLOW_COPY_AND_ASSIGN(PowerEventObserverTestApi);
+};
+
+}  // namespace ash
+
+#endif  // ASH_SYSTEM_POWER_POWER_EVENT_OBSERVER_TEST_API_H_
diff --git a/ash/system/power/power_event_observer_unittest.cc b/ash/system/power/power_event_observer_unittest.cc
index a484d1af..94ee0ea 100644
--- a/ash/system/power/power_event_observer_unittest.cc
+++ b/ash/system/power/power_event_observer_unittest.cc
@@ -8,6 +8,7 @@
 
 #include "ash/session/session_controller.h"
 #include "ash/shell.h"
+#include "ash/system/power/power_event_observer_test_api.h"
 #include "ash/test/ash_test_base.h"
 #include "ash/wm/lock_state_controller.h"
 #include "ash/wm/lock_state_controller_test_api.h"
@@ -29,6 +30,7 @@
 
   // AshTestBase:
   void SetUp() override {
+    chromeos::DBusThreadManager::Initialize();
     AshTestBase::SetUp();
     observer_.reset(new PowerEventObserver());
   }
@@ -36,6 +38,7 @@
   void TearDown() override {
     observer_.reset();
     AshTestBase::TearDown();
+    chromeos::DBusThreadManager::Shutdown();
   }
 
  protected:
@@ -78,23 +81,63 @@
   // It should run the callback when it hears that the screen is locked and the
   // lock screen animations have completed.
   BlockUserSession(BLOCKED_BY_LOCK_SCREEN);
+
+  PowerEventObserverTestApi test_api(observer_.get());
+
+  ui::Compositor* compositor =
+      Shell::GetPrimaryRootWindow()->GetHost()->compositor();
+
+  test_api.CompositingDidCommit(compositor);
   observer_->OnLockAnimationsComplete();
+
+  // Verify that CompositingStarted and CompositingEnded observed before
+  // CompositingDidCommit are ignored.
+  test_api.CompositingStarted(compositor);
+  test_api.CompositingEnded(compositor);
+  EXPECT_EQ(1, client->GetNumPendingSuspendReadinessCallbacks());
+  EXPECT_EQ(1, GetNumVisibleCompositors());
+
+  // Suspend should remain delayed after first compositing cycle ends.
+  test_api.CompositeFrame(compositor);
+  EXPECT_EQ(1, client->GetNumPendingSuspendReadinessCallbacks());
+  EXPECT_EQ(1, GetNumVisibleCompositors());
+
+  test_api.CompositingDidCommit(compositor);
+  test_api.CompositingStarted(compositor);
+  EXPECT_EQ(1, client->GetNumPendingSuspendReadinessCallbacks());
+  EXPECT_EQ(1, GetNumVisibleCompositors());
+
+  test_api.CompositingEnded(compositor);
   EXPECT_EQ(0, client->GetNumPendingSuspendReadinessCallbacks());
+  EXPECT_EQ(0, GetNumVisibleCompositors());
 
   // If the system is already locked, no callback should be requested.
   observer_->SuspendDone(base::TimeDelta());
+  EXPECT_EQ(1, GetNumVisibleCompositors());
   UnblockUserSession();
   BlockUserSession(BLOCKED_BY_LOCK_SCREEN);
+
+  // Notify that lock animation is complete.
   observer_->OnLockAnimationsComplete();
+
+  // Wait for a compositing after lock animation completes before suspending.
+  // In this case compositors should be made invisible immediately
+  test_api.CompositeFrame(compositor);
+  test_api.CompositeFrame(compositor);
+
   observer_->SuspendImminent(power_manager::SuspendImminent_Reason_OTHER);
   EXPECT_EQ(0, client->GetNumPendingSuspendReadinessCallbacks());
+  EXPECT_EQ(0, GetNumVisibleCompositors());
 
   // It also shouldn't request a callback if it isn't instructed to lock the
   // screen.
   observer_->SuspendDone(base::TimeDelta());
+  UnblockUserSession();
   SetShouldLockScreenAutomatically(false);
+  EXPECT_EQ(1, GetNumVisibleCompositors());
   observer_->SuspendImminent(power_manager::SuspendImminent_Reason_OTHER);
   EXPECT_EQ(0, client->GetNumPendingSuspendReadinessCallbacks());
+  EXPECT_EQ(0, GetNumVisibleCompositors());
 }
 
 TEST_F(PowerEventObserverTest, SetInvisibleBeforeSuspend) {
@@ -118,6 +161,10 @@
   EXPECT_EQ(1, GetNumVisibleCompositors());
 
   observer_->OnLockAnimationsComplete();
+
+  EXPECT_EQ(1, GetNumVisibleCompositors());
+  ASSERT_TRUE(PowerEventObserverTestApi(observer_.get())
+                  .SimulateCompositorsReadyForSuspend());
   EXPECT_EQ(0, GetNumVisibleCompositors());
 
   observer_->SuspendDone(base::TimeDelta());
@@ -146,9 +193,10 @@
   // - The suspend request is canceled.
   // - Another suspend request is started.
   // - The screen lock animations complete.
+  // - The screen lock UI changes get composited
   //
   // In this case, the observer should block the second suspend request until
-  // the animations have completed.
+  // the screen lock compositing is done.
   SetCanLockScreen(true);
   SetShouldLockScreenAutomatically(true);
 
@@ -168,10 +216,124 @@
   EXPECT_EQ(2, client->GetNumPendingSuspendReadinessCallbacks());
 
   observer_->OnLockAnimationsComplete();
+  EXPECT_EQ(2, client->GetNumPendingSuspendReadinessCallbacks());
+  EXPECT_EQ(1, GetNumVisibleCompositors());
+
+  ASSERT_TRUE(PowerEventObserverTestApi(observer_.get())
+                  .SimulateCompositorsReadyForSuspend());
   EXPECT_EQ(1, client->GetNumPendingSuspendReadinessCallbacks());
   EXPECT_EQ(0, GetNumVisibleCompositors());
 }
 
+// Tests that device suspend is delayed for screen lock until the screen lock
+// changes are composited for all root windows.
+TEST_F(PowerEventObserverTest, DelaySuspendForCompositing_MultiDisplay) {
+  SetCanLockScreen(true);
+  SetShouldLockScreenAutomatically(true);
+
+  UpdateDisplay("100x100,200x200");
+
+  chromeos::PowerManagerClient* client =
+      chromeos::DBusThreadManager::Get()->GetPowerManagerClient();
+  observer_->SuspendImminent(power_manager::SuspendImminent_Reason_OTHER);
+  EXPECT_EQ(1, client->GetNumPendingSuspendReadinessCallbacks());
+
+  aura::Window::Windows windows = Shell::GetAllRootWindows();
+  ASSERT_EQ(2u, windows.size());
+
+  ui::Compositor* primary_compositor = windows[0]->GetHost()->compositor();
+  ui::Compositor* secondary_compositor = windows[1]->GetHost()->compositor();
+  ASSERT_EQ(2, GetNumVisibleCompositors());
+  EXPECT_EQ(1, client->GetNumPendingSuspendReadinessCallbacks());
+
+  PowerEventObserverTestApi test_api(observer_.get());
+
+  // Simulate a commit before lock animations complete, and verify associated
+  // compositing ends are ignored.
+  test_api.CompositingDidCommit(secondary_compositor);
+  observer_->OnLockAnimationsComplete();
+
+  test_api.CompositingStarted(secondary_compositor);
+  test_api.CompositingEnded(secondary_compositor);
+
+  EXPECT_EQ(1, client->GetNumPendingSuspendReadinessCallbacks());
+  EXPECT_EQ(2, GetNumVisibleCompositors());
+
+  test_api.CompositeFrame(primary_compositor);
+  test_api.CompositeFrame(primary_compositor);
+
+  test_api.CompositeFrame(secondary_compositor);
+
+  // Even though compositing for one display is done, changes to compositor
+  // visibility, and suspend readines state should be delayed until compositing
+  // for the other display finishes.
+  EXPECT_EQ(1, client->GetNumPendingSuspendReadinessCallbacks());
+  EXPECT_EQ(2, GetNumVisibleCompositors());
+
+  test_api.CompositeFrame(secondary_compositor);
+  EXPECT_EQ(0, client->GetNumPendingSuspendReadinessCallbacks());
+  EXPECT_EQ(0, GetNumVisibleCompositors());
+}
+
+TEST_F(PowerEventObserverTest,
+       DelaySuspendForCompositing_PendingDisplayRemoved) {
+  SetCanLockScreen(true);
+  SetShouldLockScreenAutomatically(true);
+
+  UpdateDisplay("100x100,200x200");
+
+  chromeos::PowerManagerClient* client =
+      chromeos::DBusThreadManager::Get()->GetPowerManagerClient();
+  observer_->SuspendImminent(power_manager::SuspendImminent_Reason_OTHER);
+  EXPECT_EQ(1, client->GetNumPendingSuspendReadinessCallbacks());
+
+  aura::Window::Windows windows = Shell::GetAllRootWindows();
+  ASSERT_EQ(2u, windows.size());
+
+  ui::Compositor* primary_compositor = windows[0]->GetHost()->compositor();
+  ASSERT_EQ(2, GetNumVisibleCompositors());
+  EXPECT_EQ(1, client->GetNumPendingSuspendReadinessCallbacks());
+  observer_->OnLockAnimationsComplete();
+
+  PowerEventObserverTestApi test_api(observer_.get());
+
+  test_api.CompositeFrame(primary_compositor);
+  test_api.CompositeFrame(primary_compositor);
+
+  // Even though compositing for one display is done, changes to compositor
+  // visibility, and suspend readines state should be delayed until compositing
+  // for the other display finishes.
+  EXPECT_EQ(1, client->GetNumPendingSuspendReadinessCallbacks());
+  EXPECT_EQ(2, GetNumVisibleCompositors());
+
+  // Remove the second display, and verify the remaining compositor is hidden
+  // at this point.
+  UpdateDisplay("100x100");
+  base::RunLoop().RunUntilIdle();
+
+  EXPECT_EQ(0, client->GetNumPendingSuspendReadinessCallbacks());
+  EXPECT_EQ(0, GetNumVisibleCompositors());
+}
+
+TEST_F(PowerEventObserverTest, CompositorNotVisibleAtLockAnimationsComplete) {
+  SetCanLockScreen(true);
+  SetShouldLockScreenAutomatically(true);
+
+  chromeos::PowerManagerClient* client =
+      chromeos::DBusThreadManager::Get()->GetPowerManagerClient();
+  observer_->SuspendImminent(power_manager::SuspendImminent_Reason_OTHER);
+  EXPECT_EQ(1, client->GetNumPendingSuspendReadinessCallbacks());
+
+  Shell::GetPrimaryRootWindow()->GetHost()->compositor()->SetVisible(false);
+
+  observer_->OnLockAnimationsComplete();
+  EXPECT_EQ(1, client->GetNumPendingSuspendReadinessCallbacks());
+  base::RunLoop().RunUntilIdle();
+
+  EXPECT_EQ(0, client->GetNumPendingSuspendReadinessCallbacks());
+  EXPECT_EQ(0, GetNumVisibleCompositors());
+}
+
 // Tests that for suspend imminent induced locking screen, locking animations
 // are immediate.
 TEST_F(PowerEventObserverTest, ImmediateLockAnimations) {
diff --git a/ash/test/ash_test_helper.cc b/ash/test/ash_test_helper.cc
index 59899d1..f52cb56 100644
--- a/ash/test/ash_test_helper.cc
+++ b/ash/test/ash_test_helper.cc
@@ -44,6 +44,7 @@
 #include "ui/base/material_design/material_design_controller.h"
 #include "ui/base/platform_window_defaults.h"
 #include "ui/base/test/material_design_controller_test_api.h"
+#include "ui/base/ui_base_features.h"
 #include "ui/base/ui_base_switches_util.h"
 #include "ui/compositor/scoped_animation_duration_scale_mode.h"
 #include "ui/compositor/test/context_factories_for_test.h"
@@ -114,7 +115,8 @@
       ui::ScopedAnimationDurationScaleMode::ZERO_DURATION));
   ui::InitializeInputMethodForTesting();
 
-  if (config_ == Config::MUS && !::switches::IsMusHostingViz()) {
+  if (config_ == Config::MUS &&
+      !base::FeatureList::IsEnabled(features::kMash)) {
     ui::ContextFactory* context_factory = nullptr;
     ui::ContextFactoryPrivate* context_factory_private = nullptr;
     ui::InitializeContextFactoryForTests(false /* enable_pixel_output */,
@@ -264,7 +266,7 @@
 void AshTestHelper::NotifyClientAboutAcceleratedWidgets() {
   if (config_ == Config::CLASSIC)
     return;
-  if (::switches::IsMusHostingViz())
+  if (base::FeatureList::IsEnabled(features::kMash))
     return;
   Shell* shell = Shell::Get();
   window_tree_client_setup_.NotifyClientAboutAcceleratedWidgets(
diff --git a/ash/test/ash_test_suite.cc b/ash/test/ash_test_suite.cc
index 66dd6d0..ee7b43d 100644
--- a/ash/test/ash_test_suite.cc
+++ b/ash/test/ash_test_suite.cc
@@ -11,10 +11,12 @@
 #include "base/files/file_path.h"
 #include "base/i18n/rtl.h"
 #include "base/path_service.h"
+#include "base/test/scoped_feature_list.h"
 #include "build/build_config.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "ui/aura/test/aura_test_context_factory.h"
 #include "ui/base/resource/resource_bundle.h"
+#include "ui/base/ui_base_features.h"
 #include "ui/base/ui_base_paths.h"
 #include "ui/base/ui_base_switches.h"
 #include "ui/compositor/test/context_factories_for_test.h"
@@ -58,24 +60,19 @@
         ash_test_resources_200, ui::SCALE_FACTOR_200P);
   }
 
-  const bool is_mus = base::CommandLine::ForCurrentProcess()->HasSwitch("mus");
-  const bool is_mash =
-      base::CommandLine::ForCurrentProcess()->HasSwitch("mash");
+  const bool is_mus = features::IsMusEnabled();
+  const bool is_mash = base::FeatureList::IsEnabled(features::kMash);
   AshTestHelper::config_ =
-      is_mus ? Config::MUS : is_mash ? Config::MASH : Config::CLASSIC;
+      is_mash ? Config::MASH : is_mus ? Config::MUS : Config::CLASSIC;
 
   base::DiscardableMemoryAllocator::SetInstance(&discardable_memory_allocator_);
-  env_ = aura::Env::CreateInstance(is_mus || is_mash ? aura::Env::Mode::MUS
-                                                     : aura::Env::Mode::LOCAL);
+  env_ = aura::Env::CreateInstance(is_mus ? aura::Env::Mode::MUS
+                                          : aura::Env::Mode::LOCAL);
 
   if (is_mash) {
     context_factory_ = std::make_unique<aura::test::AuraTestContextFactory>();
     env_->set_context_factory(context_factory_.get());
     env_->set_context_factory_private(nullptr);
-    // mus needs to host viz, because ash by itself cannot.
-    base::CommandLine::ForCurrentProcess()->AppendSwitch(switches::kMus);
-    base::CommandLine::ForCurrentProcess()->AppendSwitch(
-        switches::kMusHostingViz);
   }
 }
 
diff --git a/ash/wallpaper/wallpaper_controller.cc b/ash/wallpaper/wallpaper_controller.cc
index f55d757..7663d07 100644
--- a/ash/wallpaper/wallpaper_controller.cc
+++ b/ash/wallpaper/wallpaper_controller.cc
@@ -639,6 +639,35 @@
   return wallpaper::WALLPAPER_TYPE_COUNT;
 }
 
+bool WallpaperController::ShouldShowInitialAnimation() {
+  // The slower initial animation is only applicable if:
+  // 1) It's the first run after system boot, not after user sign-out.
+  if (!base::CommandLine::ForCurrentProcess()->HasSwitch(
+          chromeos::switches::kFirstExecAfterBoot)) {
+    return false;
+  }
+  // 2) It's at the login screen.
+  if (Shell::Get()->session_controller()->IsActiveUserSessionStarted() ||
+      !base::CommandLine::ForCurrentProcess()->HasSwitch(
+          chromeos::switches::kLoginManager)) {
+    return false;
+  }
+  // 3) It's the first wallpaper being shown, not for the switching between
+  //    multiple user pods.
+  if (!is_first_wallpaper_)
+    return false;
+
+  return true;
+}
+
+void WallpaperController::OnWallpaperAnimationFinished() {
+  // TODO(crbug.com/784495, 776464): This is used by a code path in web-UI
+  // login. Remove it if views-based login is not interested in this event.
+  if (wallpaper_controller_client_ && is_first_wallpaper_) {
+    wallpaper_controller_client_->OnFirstWallpaperAnimationFinished();
+  }
+}
+
 void WallpaperController::SetDefaultWallpaperImpl(
     const AccountId& account_id,
     const user_manager::UserType& user_type,
@@ -730,6 +759,8 @@
     color_calculator_->RemoveObserver(this);
     color_calculator_.reset();
   }
+
+  is_first_wallpaper_ = !current_wallpaper_;
   current_wallpaper_.reset(new wallpaper::WallpaperResizer(
       image, GetMaxDisplaySizeInNative(), info, sequenced_task_runner_));
   current_wallpaper_->AddObserver(this);
@@ -737,6 +768,7 @@
 
   for (auto& observer : observers_)
     observer.OnWallpaperDataChanged();
+
   wallpaper_mode_ = WALLPAPER_IMAGE;
   InstallDesktopControllerForAllWindows();
   wallpaper_count_for_testing_++;
diff --git a/ash/wallpaper/wallpaper_controller.h b/ash/wallpaper/wallpaper_controller.h
index 1f40919..2337566 100644
--- a/ash/wallpaper/wallpaper_controller.h
+++ b/ash/wallpaper/wallpaper_controller.h
@@ -231,6 +231,14 @@
   // no wallpaper.
   wallpaper::WallpaperType GetWallpaperType() const;
 
+  // Returns true if the slower initial animation should be shown (as opposed to
+  // the faster animation that's used e.g. when switching between different
+  // wallpapers at login screen).
+  bool ShouldShowInitialAnimation();
+
+  // Notifies the controller that the wallpaper animation has finished.
+  void OnWallpaperAnimationFinished();
+
   // Returns true if the active user is allowed to open the wallpaper picker.
   bool CanOpenWallpaperPicker();
 
@@ -603,6 +611,10 @@
   // Whether the device wallpaper policy is enforced on this device.
   bool is_device_wallpaper_policy_enforced_ = false;
 
+  // Whether the current wallpaper (if any) is the first wallpaper since the
+  // controller initialization. Empty wallpapers for testing don't count.
+  bool is_first_wallpaper_ = false;
+
   scoped_refptr<base::SequencedTaskRunner> sequenced_task_runner_;
 
   ScopedSessionObserver scoped_session_observer_;
diff --git a/ash/wallpaper/wallpaper_controller_unittest.cc b/ash/wallpaper/wallpaper_controller_unittest.cc
index f2b6d18..e78abed 100644
--- a/ash/wallpaper/wallpaper_controller_unittest.cc
+++ b/ash/wallpaper/wallpaper_controller_unittest.cc
@@ -1649,4 +1649,69 @@
   EXPECT_FALSE(IsDevicePolicyWallpaper());
 }
 
+TEST_F(WallpaperControllerTest, ShouldShowInitialAnimationAfterBoot) {
+  CreateDefaultWallpapers();
+
+  // Simulate the login screen after system boot.
+  base::CommandLine::ForCurrentProcess()->AppendSwitch(
+      chromeos::switches::kFirstExecAfterBoot);
+  base::CommandLine::ForCurrentProcess()->AppendSwitch(
+      chromeos::switches::kLoginManager);
+  ClearLogin();
+
+  // Show the first wallpaper. Verify that the slower animation should be used.
+  controller_->ShowUserWallpaper(InitializeUser(account_id_1));
+  RunAllTasksUntilIdle();
+  EXPECT_TRUE(controller_->ShouldShowInitialAnimation());
+  EXPECT_EQ(1, GetWallpaperCount());
+
+  // Show the second wallpaper. Verify that the slower animation should not be
+  // used. (Use a different user type to ensure a different wallpaper is shown,
+  // otherwise requests of loading the same wallpaper are ignored.)
+  controller_->ShowUserWallpaper(
+      InitializeUser(AccountId::FromUserEmail("child@test.com")));
+  RunAllTasksUntilIdle();
+  EXPECT_FALSE(controller_->ShouldShowInitialAnimation());
+  EXPECT_EQ(1, GetWallpaperCount());
+
+  // Log in the user and show the wallpaper. Verify that the slower animation
+  // should not be used.
+  SimulateUserLogin(user_1);
+  controller_->ShowUserWallpaper(InitializeUser(account_id_1));
+  RunAllTasksUntilIdle();
+  EXPECT_FALSE(controller_->ShouldShowInitialAnimation());
+  EXPECT_EQ(1, GetWallpaperCount());
+}
+
+TEST_F(WallpaperControllerTest, ShouldNotShowInitialAnimationAfterSignOut) {
+  CreateDefaultWallpapers();
+
+  // Simulate the login screen after user sign-out. Verify that the slower
+  // animation should never be used.
+  base::CommandLine::ForCurrentProcess()->AppendSwitch(
+      chromeos::switches::kLoginManager);
+  CreateAndSaveWallpapers(account_id_1);
+  ClearLogin();
+
+  // Show the first wallpaper.
+  controller_->ShowUserWallpaper(InitializeUser(account_id_1));
+  RunAllTasksUntilIdle();
+  EXPECT_FALSE(controller_->ShouldShowInitialAnimation());
+  EXPECT_EQ(1, GetWallpaperCount());
+
+  // Show the second wallpaper.
+  controller_->ShowUserWallpaper(
+      InitializeUser(AccountId::FromUserEmail("child@test.com")));
+  RunAllTasksUntilIdle();
+  EXPECT_FALSE(controller_->ShouldShowInitialAnimation());
+  EXPECT_EQ(1, GetWallpaperCount());
+
+  // Log in the user and show the wallpaper.
+  SimulateUserLogin(user_1);
+  controller_->ShowUserWallpaper(InitializeUser(account_id_1));
+  RunAllTasksUntilIdle();
+  EXPECT_FALSE(controller_->ShouldShowInitialAnimation());
+  EXPECT_EQ(1, GetWallpaperCount());
+}
+
 }  // namespace ash
diff --git a/ash/wallpaper/wallpaper_delegate.h b/ash/wallpaper/wallpaper_delegate.h
index 12a03674..3ab2af6a 100644
--- a/ash/wallpaper/wallpaper_delegate.h
+++ b/ash/wallpaper/wallpaper_delegate.h
@@ -13,27 +13,12 @@
  public:
   virtual ~WallpaperDelegate() {}
 
-  // Returns the type of window animation that should be used when showing the
-  // wallpaper.
-  virtual int GetAnimationType() = 0;
-
   // Returns the wallpaper animation duration in ms. A value of 0 indicates
   // that the default should be used.
   virtual int GetAnimationDurationOverride() = 0;
 
   // Sets wallpaper animation duration in ms. Pass 0 to use the default.
   virtual void SetAnimationDurationOverride(int animation_duration_in_ms) = 0;
-
-  // Should the slower initial animation be shown (as opposed to the faster
-  // animation that's used e.g. when switching from one user's wallpaper to
-  // another's on the login screen)?
-  virtual bool ShouldShowInitialAnimation() = 0;
-
-  // Notifies delegate that wallpaper animation has finished.
-  virtual void OnWallpaperAnimationFinished() = 0;
-
-  // Notifies delegate that wallpaper boot animation has finished.
-  virtual void OnWallpaperBootAnimationFinished() = 0;
 };
 
 }  // namespace ash
diff --git a/ash/wallpaper/wallpaper_delegate_mus.cc b/ash/wallpaper/wallpaper_delegate_mus.cc
index a089808..4791f70 100644
--- a/ash/wallpaper/wallpaper_delegate_mus.cc
+++ b/ash/wallpaper/wallpaper_delegate_mus.cc
@@ -11,10 +11,6 @@
 WallpaperDelegateMus::WallpaperDelegateMus() = default;
 WallpaperDelegateMus::~WallpaperDelegateMus() = default;
 
-int WallpaperDelegateMus::GetAnimationType() {
-  return ::wm::WINDOW_VISIBILITY_ANIMATION_TYPE_FADE;
-}
-
 int WallpaperDelegateMus::GetAnimationDurationOverride() {
   return 0;
 }
@@ -24,12 +20,4 @@
   NOTIMPLEMENTED_LOG_ONCE();
 }
 
-bool WallpaperDelegateMus::ShouldShowInitialAnimation() {
-  return false;
-}
-
-void WallpaperDelegateMus::OnWallpaperAnimationFinished() {}
-
-void WallpaperDelegateMus::OnWallpaperBootAnimationFinished() {}
-
 }  // namespace ash
diff --git a/ash/wallpaper/wallpaper_delegate_mus.h b/ash/wallpaper/wallpaper_delegate_mus.h
index f73648b..d47f160 100644
--- a/ash/wallpaper/wallpaper_delegate_mus.h
+++ b/ash/wallpaper/wallpaper_delegate_mus.h
@@ -17,12 +17,8 @@
 
  private:
   // WallpaperDelegate overrides:
-  int GetAnimationType() override;
   int GetAnimationDurationOverride() override;
   void SetAnimationDurationOverride(int animation_duration_in_ms) override;
-  bool ShouldShowInitialAnimation() override;
-  void OnWallpaperAnimationFinished() override;
-  void OnWallpaperBootAnimationFinished() override;
 
   DISALLOW_COPY_AND_ASSIGN(WallpaperDelegateMus);
 };
diff --git a/ash/wallpaper/wallpaper_view.cc b/ash/wallpaper/wallpaper_view.cc
index d6bc3e3..92652ba 100644
--- a/ash/wallpaper/wallpaper_view.cc
+++ b/ash/wallpaper/wallpaper_view.cc
@@ -13,6 +13,7 @@
 #include "ash/wallpaper/wallpaper_delegate.h"
 #include "ash/wallpaper/wallpaper_widget_controller.h"
 #include "ash/wm/overview/window_selector_controller.h"
+#include "ash/wm/window_animation_types.h"
 #include "ui/aura/window.h"
 #include "ui/display/display.h"
 #include "ui/display/manager/display_manager.h"
@@ -237,7 +238,6 @@
 views::Widget* CreateWallpaperWidget(aura::Window* root_window,
                                      int container_id) {
   WallpaperController* controller = Shell::Get()->wallpaper_controller();
-  WallpaperDelegate* wallpaper_delegate = Shell::Get()->wallpaper_delegate();
 
   views::Widget* wallpaper_widget = new views::Widget;
   views::Widget::InitParams params(
@@ -248,7 +248,10 @@
   params.parent = root_window->GetChildById(container_id);
   wallpaper_widget->Init(params);
   wallpaper_widget->SetContentsView(new LayerControlView(new WallpaperView()));
-  int animation_type = wallpaper_delegate->GetAnimationType();
+  int animation_type =
+      controller->ShouldShowInitialAnimation()
+          ? wm::WINDOW_VISIBILITY_ANIMATION_TYPE_BRIGHTNESS_GRAYSCALE
+          : ::wm::WINDOW_VISIBILITY_ANIMATION_TYPE_FADE;
   aura::Window* wallpaper_window = wallpaper_widget->GetNativeWindow();
   ::wm::SetWindowVisibilityAnimationType(wallpaper_window, animation_type);
 
@@ -257,14 +260,15 @@
   // 2. Wallpaper fades in from a non empty background.
   // 3. From an empty background, chrome transit to a logged in user session.
   // 4. From an empty background, guest user logged in.
-  if (wallpaper_delegate->ShouldShowInitialAnimation() ||
+  if (controller->ShouldShowInitialAnimation() ||
       RootWindowController::ForWindow(root_window)
           ->wallpaper_widget_controller()
           ->IsAnimating() ||
       Shell::Get()->session_controller()->NumberOfLoggedInUsers()) {
     ::wm::SetWindowVisibilityAnimationTransition(wallpaper_window,
                                                  ::wm::ANIMATE_SHOW);
-    int duration_override = wallpaper_delegate->GetAnimationDurationOverride();
+    int duration_override =
+        Shell::Get()->wallpaper_delegate()->GetAnimationDurationOverride();
     if (duration_override) {
       ::wm::SetWindowVisibilityAnimationDuration(
           wallpaper_window,
diff --git a/ash/wallpaper/wallpaper_widget_controller.cc b/ash/wallpaper/wallpaper_widget_controller.cc
index 1e7c2b7..e9a3e009e 100644
--- a/ash/wallpaper/wallpaper_widget_controller.cc
+++ b/ash/wallpaper/wallpaper_widget_controller.cc
@@ -9,7 +9,7 @@
 #include "ash/ash_export.h"
 #include "ash/root_window_controller.h"
 #include "ash/shell.h"
-#include "ash/wallpaper/wallpaper_delegate.h"
+#include "ash/wallpaper/wallpaper_controller.h"
 #include "base/scoped_observer.h"
 #include "ui/aura/window.h"
 #include "ui/aura/window_observer.h"
@@ -241,7 +241,7 @@
     std::move(wallpaper_set_callback_).Run();
 
   // Notify observers that animation finished.
-  Shell::Get()->wallpaper_delegate()->OnWallpaperAnimationFinished();
+  Shell::Get()->wallpaper_controller()->OnWallpaperAnimationFinished();
 }
 
 }  // namespace ash
diff --git a/ash/wm/splitview/split_view_controller.cc b/ash/wm/splitview/split_view_controller.cc
index 333eea17..bd8e468 100644
--- a/ash/wm/splitview/split_view_controller.cc
+++ b/ash/wm/splitview/split_view_controller.cc
@@ -89,6 +89,22 @@
   return window1_i > window2_i ? window1 : window2;
 }
 
+mojom::SplitViewState ToMojomSplitViewState(SplitViewController::State state) {
+  switch (state) {
+    case SplitViewController::NO_SNAP:
+      return mojom::SplitViewState::NO_SNAP;
+    case SplitViewController::LEFT_SNAPPED:
+      return mojom::SplitViewState::LEFT_SNAPPED;
+    case SplitViewController::RIGHT_SNAPPED:
+      return mojom::SplitViewState::RIGHT_SNAPPED;
+    case SplitViewController::BOTH_SNAPPED:
+      return mojom::SplitViewState::BOTH_SNAPPED;
+    default:
+      NOTREACHED();
+      return mojom::SplitViewState::NO_SNAP;
+  }
+}
+
 }  // namespace
 
 SplitViewController::SplitViewController() {
@@ -125,6 +141,11 @@
              blink::kWebScreenOrientationLockPortraitSecondary;
 }
 
+void SplitViewController::BindRequest(
+    mojom::SplitViewControllerRequest request) {
+  bindings_.AddBinding(this, std::move(request));
+}
+
 bool SplitViewController::CanSnap(aura::Window* window) {
   if (!wm::CanActivateWindow(window))
     return false;
@@ -441,6 +462,12 @@
   observers_.RemoveObserver(observer);
 }
 
+void SplitViewController::AddObserver(mojom::SplitViewObserverPtr observer) {
+  mojom::SplitViewObserver* observer_ptr = observer.get();
+  mojo_observers_.AddPtr(std::move(observer));
+  observer_ptr->OnSplitViewStateChanged(ToMojomSplitViewState(state_));
+}
+
 void SplitViewController::OnWindowDestroying(aura::Window* window) {
   DCHECK(IsSplitViewModeActive());
   DCHECK(window == left_window_ || window == right_window_);
@@ -614,6 +641,10 @@
   // should notify its observers.
   for (Observer& observer : observers_)
     observer.OnSplitViewStateChanged(previous_state, state);
+  mojo_observers_.ForAllPtrs(
+      [previous_state, state](mojom::SplitViewObserver* observer) {
+        observer->OnSplitViewStateChanged(ToMojomSplitViewState(state));
+      });
 }
 
 void SplitViewController::NotifyDividerPositionChanged() {
diff --git a/ash/wm/splitview/split_view_controller.h b/ash/wm/splitview/split_view_controller.h
index ec63025..103a0598 100644
--- a/ash/wm/splitview/split_view_controller.h
+++ b/ash/wm/splitview/split_view_controller.h
@@ -6,12 +6,15 @@
 #define ASH_WM_SPLITSVIEW_SPLIT_VIEW_CONTROLLER_H_
 
 #include "ash/ash_export.h"
+#include "ash/public/interfaces/split_view.mojom.h"
 #include "ash/shell_observer.h"
 #include "ash/wm/tablet_mode/tablet_mode_observer.h"
 #include "ash/wm/window_state_observer.h"
 #include "base/macros.h"
 #include "base/observer_list.h"
 #include "base/time/time.h"
+#include "mojo/public/cpp/bindings/binding_set.h"
+#include "mojo/public/cpp/bindings/interface_ptr_set.h"
 #include "third_party/WebKit/public/platform/modules/screen_orientation/WebScreenOrientationLockType.h"
 #include "ui/aura/window_observer.h"
 #include "ui/display/display.h"
@@ -32,7 +35,8 @@
 // The controller for the split view. It snaps a window to left/right side of
 // the screen. It also observes the two snapped windows and decides when to exit
 // the split view mode.
-class ASH_EXPORT SplitViewController : public aura::WindowObserver,
+class ASH_EXPORT SplitViewController : public mojom::SplitViewController,
+                                       public aura::WindowObserver,
                                        public ash::wm::WindowStateObserver,
                                        public ::wm::ActivationChangeObserver,
                                        public ShellObserver,
@@ -72,6 +76,9 @@
   static bool IsLeftWindowOnTopOrLeftOfScreen(
       blink::WebScreenOrientationLockType screen_orientation);
 
+  // Binds the mojom::SplitViewController interface to this object.
+  void BindRequest(mojom::SplitViewControllerRequest request);
+
   // Returns true if |window| can be activated and snapped.
   bool CanSnap(aura::Window* window);
 
@@ -117,6 +124,9 @@
   void AddObserver(Observer* observer);
   void RemoveObserver(Observer* observer);
 
+  // mojom::SplitViewObserver:
+  void AddObserver(mojom::SplitViewObserverPtr observer) override;
+
   // aura::WindowObserver:
   void OnWindowDestroying(aura::Window* window) override;
 
@@ -250,6 +260,9 @@
   void StartOverview();
   void EndOverview();
 
+  // Bindings for the SplitViewController interface.
+  mojo::BindingSet<mojom::SplitViewController> bindings_;
+
   // The current left/right snapped window.
   aura::Window* left_window_ = nullptr;
   aura::Window* right_window_ = nullptr;
@@ -305,6 +318,7 @@
   aura::Window* smooth_resize_window_ = nullptr;
 
   base::ObserverList<Observer> observers_;
+  mojo::InterfacePtrSet<mojom::SplitViewObserver> mojo_observers_;
 
   DISALLOW_COPY_AND_ASSIGN(SplitViewController);
 };
diff --git a/base/feature_list.cc b/base/feature_list.cc
index 1ed8579..e38e164 100644
--- a/base/feature_list.cc
+++ b/base/feature_list.cc
@@ -23,7 +23,7 @@
 // Pointer to the FeatureList instance singleton that was set via
 // FeatureList::SetInstance(). Does not use base/memory/singleton.h in order to
 // have more control over initialization timing. Leaky.
-FeatureList* g_instance = nullptr;
+FeatureList* g_feature_list_instance = nullptr;
 
 // Tracks whether the FeatureList instance was initialized via an accessor.
 bool g_initialized_from_accessor = false;
@@ -197,20 +197,20 @@
 
 // static
 bool FeatureList::IsEnabled(const Feature& feature) {
-  if (!g_instance) {
+  if (!g_feature_list_instance) {
     g_initialized_from_accessor = true;
     return feature.default_state == FEATURE_ENABLED_BY_DEFAULT;
   }
-  return g_instance->IsFeatureEnabled(feature);
+  return g_feature_list_instance->IsFeatureEnabled(feature);
 }
 
 // static
 FieldTrial* FeatureList::GetFieldTrial(const Feature& feature) {
-  if (!g_instance) {
+  if (!g_feature_list_instance) {
     g_initialized_from_accessor = true;
     return nullptr;
   }
-  return g_instance->GetAssociatedFieldTrial(feature);
+  return g_feature_list_instance->GetAssociatedFieldTrial(feature);
 }
 
 // static
@@ -235,12 +235,12 @@
   // accessor call(s) which likely returned incorrect information.
   CHECK(!g_initialized_from_accessor);
   bool instance_existed_before = false;
-  if (g_instance) {
-    if (g_instance->initialized_from_command_line_)
+  if (g_feature_list_instance) {
+    if (g_feature_list_instance->initialized_from_command_line_)
       return false;
 
-    delete g_instance;
-    g_instance = nullptr;
+    delete g_feature_list_instance;
+    g_feature_list_instance = nullptr;
     instance_existed_before = true;
   }
 
@@ -252,16 +252,16 @@
 
 // static
 FeatureList* FeatureList::GetInstance() {
-  return g_instance;
+  return g_feature_list_instance;
 }
 
 // static
 void FeatureList::SetInstance(std::unique_ptr<FeatureList> instance) {
-  DCHECK(!g_instance);
+  DCHECK(!g_feature_list_instance);
   instance->FinalizeInitialization();
 
   // Note: Intentional leak of global singleton.
-  g_instance = instance.release();
+  g_feature_list_instance = instance.release();
 
 #if DCHECK_IS_ON() && defined(SYZYASAN)
   // Update the behaviour of LOG_DCHECK to match the Feature configuration.
@@ -280,8 +280,8 @@
 
 // static
 std::unique_ptr<FeatureList> FeatureList::ClearInstanceForTesting() {
-  FeatureList* old_instance = g_instance;
-  g_instance = nullptr;
+  FeatureList* old_instance = g_feature_list_instance;
+  g_feature_list_instance = nullptr;
   g_initialized_from_accessor = false;
   return base::WrapUnique(old_instance);
 }
@@ -289,9 +289,9 @@
 // static
 void FeatureList::RestoreInstanceForTesting(
     std::unique_ptr<FeatureList> instance) {
-  DCHECK(!g_instance);
+  DCHECK(!g_feature_list_instance);
   // Note: Intentional leak of global singleton.
-  g_instance = instance.release();
+  g_feature_list_instance = instance.release();
 }
 
 void FeatureList::FinalizeInitialization() {
diff --git a/base/files/file_util.h b/base/files/file_util.h
index 780bb22c..cd8a1ba5 100644
--- a/base/files/file_util.h
+++ b/base/files/file_util.h
@@ -185,6 +185,12 @@
 // Returns true iff |bytes| bytes have been successfully read from |fd|.
 BASE_EXPORT bool ReadFromFD(int fd, char* buffer, size_t bytes);
 
+// Performs the same function as CreateAndOpenTemporaryFileInDir(), but returns
+// the file-descriptor directly, rather than wrapping it into a FILE. Returns
+// -1 on failure.
+BASE_EXPORT int CreateAndOpenFdForTemporaryFileInDir(const FilePath& dir,
+                                                     FilePath* path);
+
 // The following functions use POSIX functionality that isn't supported by
 // Fuchsia.
 #if !defined(OS_FUCHSIA)
diff --git a/base/files/file_util_posix.cc b/base/files/file_util_posix.cc
index ad28562..2a348182 100644
--- a/base/files/file_util_posix.cc
+++ b/base/files/file_util_posix.cc
@@ -142,19 +142,6 @@
 #endif
 }
 
-// Creates and opens a temporary file in |directory|, returning the
-// file descriptor. |path| is set to the temporary file path.
-// This function does NOT unlink() the file.
-int CreateAndOpenFdForTemporaryFile(FilePath directory, FilePath* path) {
-  AssertBlockingAllowed();  // For call to mkstemp().
-  *path = directory.Append(base::TempFileName());
-  const std::string& tmpdir_string = path->value();
-  // this should be OK since mkstemp just replaces characters in place
-  char* buffer = const_cast<char*>(tmpdir_string.c_str());
-
-  return HANDLE_EINTR(mkstemp(buffer));
-}
-
 #if defined(OS_LINUX) || defined(OS_AIX)
 // Determine if /dev/shm files can be mapped and then mprotect'd PROT_EXEC.
 // This depends on the mount options used for /dev/shm, which vary among
@@ -165,7 +152,8 @@
   bool result = false;
   FilePath path;
 
-  ScopedFD fd(CreateAndOpenFdForTemporaryFile(FilePath("/dev/shm"), &path));
+  ScopedFD fd(
+      CreateAndOpenFdForTemporaryFileInDir(FilePath("/dev/shm"), &path));
   if (fd.is_valid()) {
     DeleteFile(path, false);
     long sysconf_result = sysconf(_SC_PAGESIZE);
@@ -540,6 +528,17 @@
 
 #if !defined(OS_NACL_NONSFI)
 
+int CreateAndOpenFdForTemporaryFileInDir(const FilePath& directory,
+                                         FilePath* path) {
+  AssertBlockingAllowed();  // For call to mkstemp().
+  *path = directory.Append(TempFileName());
+  const std::string& tmpdir_string = path->value();
+  // this should be OK since mkstemp just replaces characters in place
+  char* buffer = const_cast<char*>(tmpdir_string.c_str());
+
+  return HANDLE_EINTR(mkstemp(buffer));
+}
+
 #if !defined(OS_FUCHSIA)
 bool CreateSymbolicLink(const FilePath& target_path,
                         const FilePath& symlink_path) {
@@ -668,7 +667,7 @@
   FilePath directory;
   if (!GetTempDir(&directory))
     return false;
-  int fd = CreateAndOpenFdForTemporaryFile(directory, path);
+  int fd = CreateAndOpenFdForTemporaryFileInDir(directory, path);
   if (fd < 0)
     return false;
   close(fd);
@@ -676,7 +675,7 @@
 }
 
 FILE* CreateAndOpenTemporaryFileInDir(const FilePath& dir, FilePath* path) {
-  int fd = CreateAndOpenFdForTemporaryFile(dir, path);
+  int fd = CreateAndOpenFdForTemporaryFileInDir(dir, path);
   if (fd < 0)
     return nullptr;
 
@@ -688,7 +687,7 @@
 
 bool CreateTemporaryFileInDir(const FilePath& dir, FilePath* temp_file) {
   AssertBlockingAllowed();  // For call to close().
-  int fd = CreateAndOpenFdForTemporaryFile(dir, temp_file);
+  int fd = CreateAndOpenFdForTemporaryFileInDir(dir, temp_file);
   return ((fd >= 0) && !IGNORE_EINTR(close(fd)));
 }
 
diff --git a/base/memory/shared_memory_helper.cc b/base/memory/shared_memory_helper.cc
index 91893d3..f98b734 100644
--- a/base/memory/shared_memory_helper.cc
+++ b/base/memory/shared_memory_helper.cc
@@ -41,13 +41,12 @@
   // A: Because they're limited to 4mb on OS X.  FFFFFFFUUUUUUUUUUU
   FilePath directory;
   ScopedPathUnlinker path_unlinker;
-  ScopedFILE fp;
   if (!GetShmemTempDir(options.executable, &directory))
     return false;
 
-  fp.reset(base::CreateAndOpenTemporaryFileInDir(directory, path));
+  fd->reset(base::CreateAndOpenFdForTemporaryFileInDir(directory, path));
 
-  if (!fp)
+  if (!fd->is_valid())
     return false;
 
   // Deleting the file prevents anyone else from mapping it in (making it
@@ -60,10 +59,10 @@
     readonly_fd->reset(HANDLE_EINTR(open(path->value().c_str(), O_RDONLY)));
     if (!readonly_fd->is_valid()) {
       DPLOG(ERROR) << "open(\"" << path->value() << "\", O_RDONLY) failed";
+      fd->reset();
       return false;
     }
   }
-  fd->reset(fileno(fp.release()));
   return true;
 }
 
diff --git a/base/sampling_heap_profiler/sampling_heap_profiler.cc b/base/sampling_heap_profiler/sampling_heap_profiler.cc
index edd957f..754db4ea 100644
--- a/base/sampling_heap_profiler/sampling_heap_profiler.cc
+++ b/base/sampling_heap_profiler/sampling_heap_profiler.cc
@@ -59,7 +59,7 @@
 // Last generated sample ordinal number.
 uint32_t g_last_sample_ordinal = 0;
 
-SamplingHeapProfiler* g_instance;
+SamplingHeapProfiler* g_sampling_heap_profiler_instance;
 void (*g_hooks_install_callback)();
 Atomic32 g_hooks_installed;
 
@@ -179,7 +179,7 @@
 SamplingHeapProfiler::Sample::~Sample() = default;
 
 SamplingHeapProfiler::SamplingHeapProfiler() {
-  g_instance = this;
+  g_sampling_heap_profiler_instance = this;
 }
 
 // static
@@ -307,8 +307,8 @@
   accumulated -=
       base::subtle::NoBarrier_AtomicExchange(&g_bytes_left, next_interval);
 
-  g_instance->DoRecordAlloc(accumulated, size, address,
-                            kSkipBaseAllocatorFrames);
+  g_sampling_heap_profiler_instance->DoRecordAlloc(accumulated, size, address,
+                                                   kSkipBaseAllocatorFrames);
 }
 
 void SamplingHeapProfiler::RecordStackTrace(Sample* sample,
@@ -365,10 +365,10 @@
   bool maybe_sampled = true;  // Pessimistically assume allocation was sampled.
   base::subtle::Barrier_AtomicIncrement(&g_operations_in_flight, 1);
   if (LIKELY(!base::subtle::NoBarrier_Load(&g_fast_path_is_closed)))
-    maybe_sampled = g_instance->samples_.count(address);
+    maybe_sampled = g_sampling_heap_profiler_instance->samples_.count(address);
   base::subtle::Barrier_AtomicIncrement(&g_operations_in_flight, -1);
   if (maybe_sampled)
-    g_instance->DoRecordFree(address);
+    g_sampling_heap_profiler_instance->DoRecordFree(address);
 }
 
 void SamplingHeapProfiler::DoRecordFree(void* address) {
diff --git a/build/android/gyp/javac.py b/build/android/gyp/javac.py
index c34780a4e..d9cfdbf 100755
--- a/build/android/gyp/javac.py
+++ b/build/android/gyp/javac.py
@@ -568,7 +568,7 @@
     ])
 
   if options.chromium_code:
-    javac_cmd.extend(['-Xlint:unchecked'])
+    javac_cmd.extend(['-Xlint:unchecked', '-Werror'])
   else:
     # XDignore.symbol.file makes javac compile against rt.jar instead of
     # ct.sym. This means that using a java internal package/class will not
diff --git a/build/android/pylib/gtest/gtest_test_instance.py b/build/android/pylib/gtest/gtest_test_instance.py
index c3e7fb6..c97fef4 100644
--- a/build/android/pylib/gtest/gtest_test_instance.py
+++ b/build/android/pylib/gtest/gtest_test_instance.py
@@ -32,8 +32,7 @@
   'ipc_perftests',
   'ipc_tests',
   'mojo_message_pipe_perftests',
-  'mojo_public_bindings_perftests',
-  'mojo_system_unittests',
+  'mojo_unittests',
   'net_unittests'
 ]
 
diff --git a/build/config/merge_for_jumbo.py b/build/config/merge_for_jumbo.py
index ab52d67..573b747 100755
--- a/build/config/merge_for_jumbo.py
+++ b/build/config/merge_for_jumbo.py
@@ -79,7 +79,6 @@
 
     write_jumbo_files(inputs, outputs, written_input_set, written_output_set)
 
-  header_files = set([x for x in all_inputs if x.endswith(".h")])
   assert set(args.outputs) == written_output_set, "Did not fill all outputs"
   if args.verbose:
     print("Generated %s (%d files) based on %s" % (
diff --git a/build/install-build-deps.sh b/build/install-build-deps.sh
index 22e63275..df6ed90 100755
--- a/build/install-build-deps.sh
+++ b/build/install-build-deps.sh
@@ -124,7 +124,6 @@
     echo -e "ERROR: The only supported distros are\n" \
       "\tUbuntu 14.04 (trusty)\n" \
       "\tUbuntu 16.04 (xenial)\n" \
-      "\tUbuntu 17.04 (zesty)\n" \
       "\tUbuntu 17.10 (artful)\n" \
       "\tDebian 8 (jessie) or later" >&2
     exit 1
@@ -143,7 +142,11 @@
 fi
 
 # Packages needed for chromeos only
-chromeos_dev_list="libbluetooth-dev libxkbcommon-dev realpath"
+chromeos_dev_list="libbluetooth-dev libxkbcommon-dev"
+
+if package_exists realpath; then
+  chromeos_dev_list="${chromeos_dev_list} realpath"
+fi
 
 # Packages needed for development
 dev_list="\
@@ -351,7 +354,9 @@
 if package_exists libglib2.0-0-dbg; then
   dbg_list="${dbg_list} libglib2.0-0-dbg"
 fi
-if package_exists libxcursor1-dbg; then
+if package_exists libxcursor1-dbgsym; then
+  dbg_list="${dbg_list} libxcursor1-dbgsym"
+elif package_exists libxcursor1-dbg; then
   dbg_list="${dbg_list} libxcursor1-dbg"
 fi
 
diff --git a/cc/ipc/cc_param_traits_macros.h b/cc/ipc/cc_param_traits_macros.h
index 520c1a1..1812958 100644
--- a/cc/ipc/cc_param_traits_macros.h
+++ b/cc/ipc/cc_param_traits_macros.h
@@ -182,8 +182,6 @@
   IPC_STRUCT_TRAITS_MEMBER(root_layer_size)
   IPC_STRUCT_TRAITS_MEMBER(min_page_scale_factor)
   IPC_STRUCT_TRAITS_MEMBER(max_page_scale_factor)
-  IPC_STRUCT_TRAITS_MEMBER(root_overflow_x_hidden)
-  IPC_STRUCT_TRAITS_MEMBER(root_overflow_y_hidden)
   IPC_STRUCT_TRAITS_MEMBER(root_overflow_y_hidden)
   IPC_STRUCT_TRAITS_MEMBER(may_contain_video)
   IPC_STRUCT_TRAITS_MEMBER(
diff --git a/cc/resources/resource_pool.cc b/cc/resources/resource_pool.cc
index acfe2be..a2512ca 100644
--- a/cc/resources/resource_pool.cc
+++ b/cc/resources/resource_pool.cc
@@ -189,7 +189,9 @@
                                              new_invalidated_rect);
 
       // Return the resource with the smallest invalidation.
-      int area = resource->invalidated_rect().size().GetArea();
+      int area =
+          resource->invalidated_rect().size().GetCheckedArea().ValueOrDefault(
+              std::numeric_limits<int>::max());
       if (iter_resource_to_return == unused_resources_.end() ||
           area < minimum_area) {
         iter_resource_to_return = it;
diff --git a/cc/resources/resource_pool_unittest.cc b/cc/resources/resource_pool_unittest.cc
index 6c86025..f42136d 100644
--- a/cc/resources/resource_pool_unittest.cc
+++ b/cc/resources/resource_pool_unittest.cc
@@ -340,6 +340,40 @@
   resource_pool_->ReleaseResource(std::move(reacquired_resource));
 }
 
+TEST_F(ResourcePoolTest, LargeInvalidatedRect) {
+  gfx::Size size(100, 100);
+  viz::ResourceFormat format = viz::RGBA_8888;
+  gfx::ColorSpace color_space;
+  uint64_t content_ids[] = {42, 43, 44};
+  // This rect is too large to take the area of it.
+  gfx::Rect large_invalidated_rect(0, 0, std::numeric_limits<int>::max() / 2,
+                                   std::numeric_limits<int>::max() / 2);
+
+  // Acquire a resource with the first content id.
+  ResourcePool::InUsePoolResource resource =
+      resource_pool_->AcquireResource(size, format, color_space);
+  resource_pool_->OnContentReplaced(resource, content_ids[0]);
+
+  // Set an invalidated rect on the resource.
+  gfx::Rect new_invalidated_rect;
+  ResourcePool::InUsePoolResource reacquired_resource =
+      resource_pool_->TryAcquireResourceForPartialRaster(
+          content_ids[1], large_invalidated_rect, content_ids[0],
+          &new_invalidated_rect);
+  EXPECT_FALSE(!!reacquired_resource);
+
+  // Release the original resource, returning it to the unused pool.
+  resource_pool_->ReleaseResource(std::move(resource));
+
+  // Try to get the resource again, this should work even though the area was
+  // too large to compute the area for.
+  resource = resource_pool_->TryAcquireResourceForPartialRaster(
+      content_ids[2], large_invalidated_rect, content_ids[1],
+      &new_invalidated_rect);
+  EXPECT_TRUE(!!resource);
+  resource_pool_->ReleaseResource(std::move(resource));
+}
+
 TEST_F(ResourcePoolTest, ReuseResource) {
   viz::ResourceFormat format = viz::RGBA_8888;
   gfx::ColorSpace color_space = gfx::ColorSpace::CreateSRGB();
diff --git a/cc/trees/layer_tree_host_impl.cc b/cc/trees/layer_tree_host_impl.cc
index 675a61e..439390a 100644
--- a/cc/trees/layer_tree_host_impl.cc
+++ b/cc/trees/layer_tree_host_impl.cc
@@ -1813,8 +1813,6 @@
   active_tree_->GetViewportSelection(&metadata.selection);
 
   if (const auto* outer_viewport_scroll_node = OuterViewportScrollNode()) {
-    metadata.root_overflow_x_hidden =
-        !outer_viewport_scroll_node->user_scrollable_horizontal;
     metadata.root_overflow_y_hidden =
         !outer_viewport_scroll_node->user_scrollable_vertical;
   }
@@ -1831,8 +1829,6 @@
   if (!inner_viewport_scroll_node)
     return metadata;
 
-  metadata.root_overflow_x_hidden |=
-      !inner_viewport_scroll_node->user_scrollable_horizontal;
   metadata.root_overflow_y_hidden |=
       !inner_viewport_scroll_node->user_scrollable_vertical;
 
diff --git a/cc/trees/layer_tree_host_impl_unittest.cc b/cc/trees/layer_tree_host_impl_unittest.cc
index d83bc5e..522e3de2 100644
--- a/cc/trees/layer_tree_host_impl_unittest.cc
+++ b/cc/trees/layer_tree_host_impl_unittest.cc
@@ -4338,7 +4338,6 @@
     EXPECT_EQ(gfx::SizeF(100.f, 100.f), metadata.root_layer_size);
     EXPECT_EQ(0.5f, metadata.min_page_scale_factor);
     EXPECT_EQ(4.f, metadata.max_page_scale_factor);
-    EXPECT_FALSE(metadata.root_overflow_x_hidden);
     EXPECT_FALSE(metadata.root_overflow_y_hidden);
   }
 
@@ -4371,7 +4370,6 @@
     host_impl_->active_tree()->BuildPropertyTreesForTesting();
     viz::CompositorFrameMetadata metadata =
         host_impl_->MakeCompositorFrameMetadata();
-    EXPECT_TRUE(metadata.root_overflow_x_hidden);
     EXPECT_FALSE(metadata.root_overflow_y_hidden);
 
     host_impl_->active_tree()
@@ -4380,7 +4378,6 @@
         ->user_scrollable_vertical = false;
     host_impl_->active_tree()->BuildPropertyTreesForTesting();
     metadata = host_impl_->MakeCompositorFrameMetadata();
-    EXPECT_TRUE(metadata.root_overflow_x_hidden);
     EXPECT_TRUE(metadata.root_overflow_y_hidden);
   }
 
@@ -4397,7 +4394,6 @@
     host_impl_->active_tree()->BuildPropertyTreesForTesting();
     viz::CompositorFrameMetadata metadata =
         host_impl_->MakeCompositorFrameMetadata();
-    EXPECT_FALSE(metadata.root_overflow_x_hidden);
     EXPECT_FALSE(metadata.root_overflow_y_hidden);
   }
 
@@ -4411,7 +4407,6 @@
     host_impl_->active_tree()->BuildPropertyTreesForTesting();
     viz::CompositorFrameMetadata metadata =
         host_impl_->MakeCompositorFrameMetadata();
-    EXPECT_TRUE(metadata.root_overflow_x_hidden);
     EXPECT_FALSE(metadata.root_overflow_y_hidden);
 
     host_impl_->active_tree()
@@ -4420,7 +4415,6 @@
         ->user_scrollable_vertical = false;
     host_impl_->active_tree()->BuildPropertyTreesForTesting();
     metadata = host_impl_->MakeCompositorFrameMetadata();
-    EXPECT_TRUE(metadata.root_overflow_x_hidden);
     EXPECT_TRUE(metadata.root_overflow_y_hidden);
   }
 
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/AppHooks.java b/chrome/android/java/src/org/chromium/chrome/browser/AppHooks.java
index 8562af0..b8fdb57 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/AppHooks.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/AppHooks.java
@@ -32,7 +32,6 @@
 import org.chromium.chrome.browser.locale.LocaleManager;
 import org.chromium.chrome.browser.metrics.VariationsSession;
 import org.chromium.chrome.browser.multiwindow.MultiWindowUtils;
-import org.chromium.chrome.browser.net.qualityprovider.ExternalEstimateProviderAndroid;
 import org.chromium.chrome.browser.offlinepages.CCTRequestStatus;
 import org.chromium.chrome.browser.omaha.RequestGenerator;
 import org.chromium.chrome.browser.partnerbookmarks.PartnerBookmark;
@@ -153,14 +152,6 @@
     }
 
     /**
-     * @return A provider of external estimates.
-     * @param nativePtr Pointer to the native ExternalEstimateProviderAndroid object.
-     */
-    public ExternalEstimateProviderAndroid createExternalEstimateProviderAndroid(long nativePtr) {
-        return new ExternalEstimateProviderAndroid(nativePtr) {};
-    }
-
-    /**
      * @return An instance of {@link FeedbackReporter} to report feedback.
      */
     public FeedbackReporter createFeedbackReporter() {
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/ChromeActivity.java b/chrome/android/java/src/org/chromium/chrome/browser/ChromeActivity.java
index 34a78782..c759875 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/ChromeActivity.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/ChromeActivity.java
@@ -789,9 +789,28 @@
      * @param tab The tab that is currently showing.
      * @param color The color that the status bar should be set to.
      */
-    protected void setStatusBarColor(Tab tab, int color) {
-        int statusBarColor = (tab != null && tab.isDefaultThemeColor())
-                ? Color.BLACK : ColorUtils.getDarkenedColorForStatusBar(color);
+    protected void setStatusBarColor(@Nullable Tab tab, int color) {
+        boolean useModernDesign =
+                supportsModernDesign() && FeatureUtilities.isChromeModernDesignEnabled();
+        int statusBarColor = color;
+        boolean supportsDarkStatusIcons = Build.VERSION.SDK_INT >= Build.VERSION_CODES.M;
+        View root = getWindow().getDecorView().getRootView();
+        if (useModernDesign && supportsDarkStatusIcons) {
+            int systemUiVisibility = root.getSystemUiVisibility();
+            boolean needsDarkStatusBarIcons =
+                    !ColorUtils.shouldUseLightForegroundOnBackground(statusBarColor);
+            if (needsDarkStatusBarIcons) {
+                systemUiVisibility |= View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR;
+            } else if (!needsDarkStatusBarIcons) {
+                systemUiVisibility &= ~View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR;
+            }
+            root.setSystemUiVisibility(systemUiVisibility);
+        } else {
+            statusBarColor = (tab != null && tab.isDefaultThemeColor())
+                    ? Color.BLACK
+                    : ColorUtils.getDarkenedColorForStatusBar(color);
+        }
+
         ApiCompatibilityUtils.setStatusBarColor(getWindow(), statusBarColor);
     }
 
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/ChromeTabbedActivity.java b/chrome/android/java/src/org/chromium/chrome/browser/ChromeTabbedActivity.java
index 416f07f..f1647644 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/ChromeTabbedActivity.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/ChromeTabbedActivity.java
@@ -2264,7 +2264,7 @@
         if (getFindToolbarManager() != null) getFindToolbarManager().hideToolbar();
         if (getAssistStatusHandler() != null) getAssistStatusHandler().updateAssistState();
         if (getAppMenuHandler() != null) getAppMenuHandler().hideAppMenu();
-        ApiCompatibilityUtils.setStatusBarColor(getWindow(), Color.BLACK);
+        setStatusBarColor(null, Color.BLACK);
         StartupMetrics.getInstance().recordOpenedTabSwitcher();
     }
 
@@ -2285,9 +2285,17 @@
     }
 
     @Override
-    protected void setStatusBarColor(Tab tab, int color) {
+    protected void setStatusBarColor(@Nullable Tab tab, int color) {
         if (DeviceFormFactor.isTablet()) return;
-        super.setStatusBarColor(tab, isInOverviewMode() ? Color.BLACK : color);
+
+        int tabSwitcherColor = Color.BLACK;
+        boolean supportsDarkStatusIcons = Build.VERSION.SDK_INT >= Build.VERSION_CODES.M;
+        if (supportsDarkStatusIcons && supportsModernDesign()
+                && FeatureUtilities.isChromeModernDesignEnabled()) {
+            tabSwitcherColor =
+                    ApiCompatibilityUtils.getColor(getResources(), R.color.modern_primary_color);
+        }
+        super.setStatusBarColor(tab, isInOverviewMode() ? tabSwitcherColor : color);
     }
 
     @Override
@@ -2399,9 +2407,7 @@
 
     @Override
     protected ChromeFullscreenManager createFullscreenManager() {
-        return new ChromeFullscreenManager(this, FeatureUtilities.isChromeHomeEnabled()
-                        ? ChromeFullscreenManager.CONTROLS_POSITION_BOTTOM
-                        : ChromeFullscreenManager.CONTROLS_POSITION_TOP);
+        return new ChromeFullscreenManager(this, ChromeFullscreenManager.CONTROLS_POSITION_TOP);
     }
 
     /**
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/SwipeRefreshHandler.java b/chrome/android/java/src/org/chromium/chrome/browser/SwipeRefreshHandler.java
index b62a963c..d716f34 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/SwipeRefreshHandler.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/SwipeRefreshHandler.java
@@ -12,7 +12,6 @@
 import org.chromium.base.metrics.RecordUserAction;
 import org.chromium.chrome.R;
 import org.chromium.chrome.browser.feature_engagement.TrackerFactory;
-import org.chromium.chrome.browser.fullscreen.FullscreenManager;
 import org.chromium.chrome.browser.profiles.Profile;
 import org.chromium.chrome.browser.tab.Tab;
 import org.chromium.components.feature_engagement.EventConstants;
@@ -120,15 +119,6 @@
 
     @Override
     public boolean start() {
-        FullscreenManager manager = mTab.getFullscreenManager();
-
-        // If the controls are at the bottom and hidden, allow cc to handle the scroll event to show
-        // them.
-        if (manager != null && manager.areBrowserControlsAtBottom()
-                && manager.getBrowserControlHiddenRatio() > 0) {
-            return false;
-        }
-
         if (mTab.getActivity() != null && mTab.getActivity().getBottomSheet() != null) {
             Tracker tracker = TrackerFactory.getTrackerForProfile(Profile.getLastUsedProfile());
             tracker.notifyEvent(EventConstants.PULL_TO_REFRESH);
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/browseractions/BrowserActionsContextMenuHelper.java b/chrome/android/java/src/org/chromium/chrome/browser/browseractions/BrowserActionsContextMenuHelper.java
index a2b27a33d..a26b9d9 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/browseractions/BrowserActionsContextMenuHelper.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/browseractions/BrowserActionsContextMenuHelper.java
@@ -241,9 +241,9 @@
                     }
                 }
             }
-            items.add(
-                    new BrowserActionsCustomContextMenuItem(CUSTOM_BROWSER_ACTIONS_ID_GROUP.get(i),
-                            customItems.get(i).getTitle(), drawable));
+            items.add(new BrowserActionsCustomContextMenuItem(
+                    CUSTOM_BROWSER_ACTIONS_ID_GROUP.get(i), customItems.get(i).getTitle(), drawable,
+                    customItems.get(i).getIconUri()));
             mCustomItemActionMap.put(
                     CUSTOM_BROWSER_ACTIONS_ID_GROUP.get(i), customItems.get(i).getAction());
         }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/browseractions/BrowserActionsCustomContextMenuItem.java b/chrome/android/java/src/org/chromium/chrome/browser/browseractions/BrowserActionsCustomContextMenuItem.java
index 2c68378..49672ce 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/browseractions/BrowserActionsCustomContextMenuItem.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/browseractions/BrowserActionsCustomContextMenuItem.java
@@ -5,10 +5,17 @@
 package org.chromium.chrome.browser.browseractions;
 
 import android.content.Context;
+import android.graphics.Bitmap;
+import android.graphics.drawable.BitmapDrawable;
 import android.graphics.drawable.Drawable;
+import android.net.Uri;
+import android.os.AsyncTask;
 import android.support.annotation.IdRes;
 import android.support.customtabs.browseractions.BrowserActionItem;
+import android.support.customtabs.browseractions.BrowserServiceImageReadTask;
 
+import org.chromium.base.Callback;
+import org.chromium.base.VisibleForTesting;
 import org.chromium.chrome.browser.contextmenu.ContextMenuItem;
 
 /**
@@ -18,18 +25,21 @@
     @IdRes
     private final int mMenuId;
     private final String mTitle;
-    private final Drawable mIcon;
+    private final Uri mIconUri;
+    private Drawable mIcon;
 
     /**
      * Constructor to build a custom context menu item from {@link BrowserActionItem}.
      * @param id The {@link IdRes} of the custom context menu item.
      * @param title The title of the custom context menu item.
      * @param icon The icon of the custom context menu item.
+     * @param iconUri The {@link Uri} used to access the icon of the custom context menu item.
      */
-    BrowserActionsCustomContextMenuItem(@IdRes int id, String title, Drawable icon) {
+    BrowserActionsCustomContextMenuItem(@IdRes int id, String title, Drawable icon, Uri iconUri) {
         mMenuId = id;
         mTitle = title;
         mIcon = icon;
+        mIconUri = iconUri;
     }
 
     @Override
@@ -43,7 +53,35 @@
     }
 
     @Override
-    public Drawable getDrawable(Context context) {
-        return mIcon;
+    public void getDrawableAsync(Context context, Callback<Drawable> callback) {
+        if (mIconUri != null) {
+            BrowserServiceImageReadTask task =
+                    new BrowserServiceImageReadTask(context.getContentResolver()) {
+                        @Override
+                        protected void onBitmapFileReady(Bitmap bitmap) {
+                            Drawable drawable = new BitmapDrawable(context.getResources(), bitmap);
+                            callback.onResult(drawable);
+                        }
+
+                    };
+            task.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, mIconUri);
+        } else {
+            callback.onResult(mIcon);
+        }
+    }
+
+    /**
+     * Set the drawable icon of custom context menu item.
+     */
+    @VisibleForTesting
+    void setDrawable(Drawable drawable) {
+        mIcon = drawable;
+    }
+
+    /**
+     * @return the {@link Uri} used to access the icon of custom context menu item.
+     */
+    public Uri getIconUri() {
+        return mIconUri;
     }
 }
\ No newline at end of file
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/compositor/animation/CompositorAnimationHandler.java b/chrome/android/java/src/org/chromium/chrome/browser/compositor/animation/CompositorAnimationHandler.java
index 379317f4..b65af7e1 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/compositor/animation/CompositorAnimationHandler.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/compositor/animation/CompositorAnimationHandler.java
@@ -39,6 +39,9 @@
     /** Whether or not testing mode is enabled. In this mode, animations end immediately. */
     private boolean mIsInTestingMode;
 
+    /** The last time that an update was pushed to animations. */
+    private long mLastUpdateTimeMs;
+
     /**
      * Default constructor.
      * @param host A {@link LayoutUpdateHost} responsible for requesting frames when an animation
@@ -54,6 +57,10 @@
      * @param animator The animator to start.
      */
     public final void registerAndStartAnimator(final CompositorAnimator animator) {
+        // If animations are currently running, the last updated time is being updated. If not,
+        // reset the value here. This prevents gaps in animations from breaking timing.
+        if (getActiveAnimationCount() <= 0) mLastUpdateTimeMs = System.currentTimeMillis();
+
         animator.addListener(new AnimatorListenerAdapter() {
             @Override
             public void onAnimationEnd(Animator a) {
@@ -74,10 +81,22 @@
 
     /**
      * Push an update to all the currently running animators.
+     * @return True if all animations controlled by this handler have completed.
+     */
+    public final boolean pushUpdate() {
+        long currentTime = System.currentTimeMillis();
+        long deltaTimeMs = currentTime - mLastUpdateTimeMs;
+        mLastUpdateTimeMs = currentTime;
+
+        return pushUpdate(deltaTimeMs);
+    }
+
+    /**
+     * Push an update to all the currently running animators.
      * @param deltaTimeMs The time since the previous update in ms.
      * @return True if all animations controlled by this handler have completed.
      */
-    public final boolean pushUpdate(long deltaTimeMs) {
+    final boolean pushUpdate(long deltaTimeMs) {
         mWasUpdateRequestedForAnimationStart = false;
         if (mAnimators.isEmpty()) return true;
 
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/compositor/layouts/LayoutManager.java b/chrome/android/java/src/org/chromium/chrome/browser/compositor/layouts/LayoutManager.java
index 97c910a1..0975719e 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/compositor/layouts/LayoutManager.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/compositor/layouts/LayoutManager.java
@@ -338,7 +338,9 @@
         if (!mUpdateRequested) return false;
         mUpdateRequested = false;
 
-        boolean areAnimatorsComplete = mAnimationHandler.pushUpdate(dtMs);
+        // TODO(mdjones): Remove the time related params from this method. The new animation system
+        // has its own timer.
+        boolean areAnimatorsComplete = mAnimationHandler.pushUpdate();
 
         final Layout layout = getActiveLayout();
         if (layout != null && layout.onUpdate(timeMs, dtMs) && layout.isHiding()
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/compositor/layouts/ToolbarSwipeLayout.java b/chrome/android/java/src/org/chromium/chrome/browser/compositor/layouts/ToolbarSwipeLayout.java
index 267602a7..d812cbf 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/compositor/layouts/ToolbarSwipeLayout.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/compositor/layouts/ToolbarSwipeLayout.java
@@ -189,10 +189,6 @@
         assert layoutTab != null;
         if (layoutTab.shouldStall()) layoutTab.setSaturation(0.0f);
         float heightDp = layoutTab.getOriginalContentHeight();
-        // Clip the layout tab so it doesn't leak into the toolbar if it's at the bottom
-        if (getFullscreenManager() != null && getFullscreenManager().areBrowserControlsAtBottom()) {
-            heightDp = heightDp - getFullscreenManager().getBottomControlsHeight() / mDpToPx;
-        }
         layoutTab.setClipSize(layoutTab.getOriginalContentWidth(), heightDp);
         layoutTab.setScale(1.f);
         layoutTab.setBorderScale(1.f);
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/compositor/scene_layer/ToolbarSceneLayer.java b/chrome/android/java/src/org/chromium/chrome/browser/compositor/scene_layer/ToolbarSceneLayer.java
index 2fb2fdd..fa8e309 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/compositor/scene_layer/ToolbarSceneLayer.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/compositor/scene_layer/ToolbarSceneLayer.java
@@ -82,8 +82,7 @@
 
         if (fullscreenManager == null) return;
         ControlContainer toolbarContainer = fullscreenManager.getControlContainer();
-        if (!isTablet && toolbarContainer != null
-                && !fullscreenManager.areBrowserControlsAtBottom()) {
+        if (!isTablet && toolbarContainer != null) {
             if (mProgressBarDrawingInfo == null) mProgressBarDrawingInfo = new DrawingInfo();
             toolbarContainer.getProgressBarDrawingInfo(mProgressBarDrawingInfo);
         } else {
@@ -96,11 +95,6 @@
         boolean showShadow = fullscreenManager.drawControlsAsTexture()
                 || forceHideAndroidBrowserControls;
 
-        // Use either top or bottom offset depending on the browser controls state.
-        float controlsOffset = fullscreenManager.areBrowserControlsAtBottom()
-                ? fullscreenManager.getBottomControlOffset()
-                : fullscreenManager.getTopControlOffset();
-
         int textBoxColor = Color.WHITE;
         int textBoxResourceId = R.drawable.card_single;
 
@@ -121,7 +115,8 @@
 
         nativeUpdateToolbarLayer(mNativePtr, resourceManager, R.id.control_container,
                 browserControlsBackgroundColor, textBoxResourceId, browserControlsUrlBarAlpha,
-                textBoxColor, controlsOffset, windowHeight, useTexture, showShadow, useModern);
+                textBoxColor, fullscreenManager.getTopControlOffset(), windowHeight, useTexture,
+                showShadow, useModern);
 
         if (mProgressBarDrawingInfo == null) return;
         nativeUpdateProgressBar(mNativePtr, mProgressBarDrawingInfo.progressBarRect.left,
@@ -167,20 +162,13 @@
                 mLayoutProvider.getActiveLayout().forceHideBrowserControlsAndroidView();
         ViewportMode viewportMode = mLayoutProvider.getActiveLayout().getViewportMode();
 
-        // TODO(mdjones): Create a "theme provider" to handle cases like this.
-        int color = mRenderHost.getBrowserControlsBackgroundColor();
-        float alpha = mRenderHost.getBrowserControlsUrlBarAlpha();
-        ChromeFullscreenManager fullscreenManager = mLayoutProvider.getFullscreenManager();
-        if (fullscreenManager.areBrowserControlsAtBottom() && fullscreenManager.getTab() != null) {
-            color = fullscreenManager.getTab().getDefaultThemeColor();
-            if (!fullscreenManager.getTab().isIncognito()) alpha = 1f;
-        }
-
         // In Chrome modern design, the url bar is always opaque since it is drawn in the
         // compositor.
+        float alpha = mRenderHost.getBrowserControlsUrlBarAlpha();
         if (FeatureUtilities.isChromeModernDesignEnabled()) alpha = 1;
 
-        update(color, alpha, mLayoutProvider.getFullscreenManager(), resourceManager,
+        update(mRenderHost.getBrowserControlsBackgroundColor(), alpha,
+                mLayoutProvider.getFullscreenManager(), resourceManager,
                 forceHideBrowserControlsAndroidView, viewportMode, DeviceFormFactor.isTablet(),
                 viewport.height());
 
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/contextmenu/ChromeContextMenuItem.java b/chrome/android/java/src/org/chromium/chrome/browser/contextmenu/ChromeContextMenuItem.java
index f9f1106..c0d3a90a 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/contextmenu/ChromeContextMenuItem.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/contextmenu/ChromeContextMenuItem.java
@@ -11,6 +11,7 @@
 import android.support.annotation.StringRes;
 import android.support.v7.content.res.AppCompatResources;
 
+import org.chromium.base.Callback;
 import org.chromium.chrome.R;
 import org.chromium.chrome.browser.DefaultBrowserInfo;
 import org.chromium.chrome.browser.search_engines.TemplateUrlService;
@@ -123,24 +124,19 @@
         return context.getString(mStringId);
     }
 
-    /**
-     * Returns the drawable and the content description associated with the context menu. If no
-     * drawable is associated with the icon, null is returned for the drawable and the
-     * iconDescription.
-     */
     @Override
-    public Drawable getDrawable(Context context) {
+    public void getDrawableAsync(Context context, Callback<Drawable> callback) {
+        Drawable drawable = null;
         if (mIconId == R.drawable.context_menu_new_tab
                 || mIconId == R.drawable.context_menu_add_to_contacts
                 || mIconId == R.drawable.context_menu_load_image
                 || mIconId == R.drawable.ic_content_copy_black) {
-            return AppCompatResources.getDrawable(context, mIconId);
-        } else if (mIconId == 0) {
-            return null;
-        } else {
-            return TintedDrawable.constructTintedDrawable(
+            drawable = AppCompatResources.getDrawable(context, mIconId);
+        } else if (mIconId != 0) {
+            drawable = TintedDrawable.constructTintedDrawable(
                     context.getResources(), mIconId, R.color.light_normal_color);
         }
+        callback.onResult(drawable);
     }
 
     @Override
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/contextmenu/ContextMenuItem.java b/chrome/android/java/src/org/chromium/chrome/browser/contextmenu/ContextMenuItem.java
index c2eeffed..5803bee 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/contextmenu/ContextMenuItem.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/contextmenu/ContextMenuItem.java
@@ -8,6 +8,8 @@
 import android.graphics.drawable.Drawable;
 import android.support.annotation.IdRes;
 
+import org.chromium.base.Callback;
+
 /**
  * An interface to get information of context menu.
  */
@@ -26,9 +28,9 @@
     String getTitle(Context context);
 
     /**
-     * Gets the {@link Drawable} icon of a context menu item.
+     * Gets the {@link Drawable} icon of a context menu item asynchronously.
      * @param context The context required to get the icon from resources.
-     * @return The icon of the menu item.
+     * @param callback The {@link Callback} used to show the icon when it is ready.
      */
-    Drawable getDrawable(Context context);
+    void getDrawableAsync(Context context, Callback<Drawable> callback);
 }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/contextmenu/ShareContextMenuItem.java b/chrome/android/java/src/org/chromium/chrome/browser/contextmenu/ShareContextMenuItem.java
index c3741ea9..38d1b0a 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/contextmenu/ShareContextMenuItem.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/contextmenu/ShareContextMenuItem.java
@@ -11,6 +11,7 @@
 import android.support.annotation.StringRes;
 import android.util.Pair;
 
+import org.chromium.base.Callback;
 import org.chromium.chrome.R;
 import org.chromium.chrome.browser.share.ShareHelper;
 import org.chromium.chrome.browser.widget.TintedDrawable;
@@ -57,9 +58,10 @@
     }
 
     @Override
-    public Drawable getDrawable(Context context) {
-        return TintedDrawable.constructTintedDrawable(
+    public void getDrawableAsync(Context context, Callback<Drawable> callback) {
+        Drawable drawable = TintedDrawable.constructTintedDrawable(
                 context.getResources(), mIconId, R.color.light_normal_color);
+        callback.onResult(drawable);
     }
 
     @Override
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/contextmenu/TabularContextMenuListAdapter.java b/chrome/android/java/src/org/chromium/chrome/browser/contextmenu/TabularContextMenuListAdapter.java
index 771ae45..f3ec4a4 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/contextmenu/TabularContextMenuListAdapter.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/contextmenu/TabularContextMenuListAdapter.java
@@ -7,6 +7,7 @@
 import android.app.Activity;
 import android.graphics.drawable.Drawable;
 import android.os.StrictMode;
+import android.text.TextUtils;
 import android.util.Pair;
 import android.view.LayoutInflater;
 import android.view.View;
@@ -86,10 +87,21 @@
             }
         }
 
-        viewHolder.mText.setText(menuItem.getTitle(mActivity));
-        Drawable icon = menuItem.getDrawable(mActivity);
-        viewHolder.mIcon.setImageDrawable(icon);
-        viewHolder.mIcon.setVisibility(icon != null ? View.VISIBLE : View.INVISIBLE);
+        final String titleText = menuItem.getTitle(mActivity);
+        viewHolder.mText.setText(titleText);
+        viewHolder.mIcon.setVisibility(View.INVISIBLE);
+        viewHolder.mIcon.setImageDrawable(null);
+        Callback<Drawable> callback = new Callback<Drawable>() {
+            @Override
+            public void onResult(Drawable drawable) {
+                if (!TextUtils.equals(titleText, viewHolder.mText.getText())) return;
+                if (drawable != null) {
+                    viewHolder.mIcon.setVisibility(View.VISIBLE);
+                    viewHolder.mIcon.setImageDrawable(drawable);
+                }
+            }
+        };
+        menuItem.getDrawableAsync(mActivity, callback);
 
         if (menuItem instanceof ShareContextMenuItem) {
             StrictMode.ThreadPolicy oldPolicy = StrictMode.allowThreadDiskReads();
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/contextualsearch/ContextualSearchTabHelper.java b/chrome/android/java/src/org/chromium/chrome/browser/contextualsearch/ContextualSearchTabHelper.java
index e5145600..fc68c94 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/contextualsearch/ContextualSearchTabHelper.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/contextualsearch/ContextualSearchTabHelper.java
@@ -241,6 +241,11 @@
                 controller.setSelectionClient(
                         mSelectionClientManager.removeContextualSearchSelectionClient());
             }
+            // Also make sure the UI is hidden if the device is offline.
+            ContextualSearchManager contextualSearchManager = getContextualSearchManager(mTab);
+            if (contextualSearchManager != null && !isDeviceOnline(contextualSearchManager)) {
+                contextualSearchManager.hideContextualSearch(StateChangeReason.UNKNOWN);
+            }
         }
     }
 
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/fullscreen/ChromeFullscreenManager.java b/chrome/android/java/src/org/chromium/chrome/browser/fullscreen/ChromeFullscreenManager.java
index 44540d6..71d7c57 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/fullscreen/ChromeFullscreenManager.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/fullscreen/ChromeFullscreenManager.java
@@ -74,15 +74,13 @@
     private final ArrayList<FullscreenListener> mListeners = new ArrayList<>();
 
     @Retention(RetentionPolicy.SOURCE)
-    @IntDef({CONTROLS_POSITION_TOP, CONTROLS_POSITION_BOTTOM, CONTROLS_POSITION_NONE})
+    @IntDef({CONTROLS_POSITION_TOP, CONTROLS_POSITION_NONE})
     public @interface ControlsPosition {}
 
     /** Controls are at the top, eg normal ChromeTabbedActivity. */
     public static final int CONTROLS_POSITION_TOP = 0;
-    /** Controls are at the bottom, eg ChromeTabbedActivity with Chrome Home enabled. */
-    public static final int CONTROLS_POSITION_BOTTOM = 1;
     /** Controls are not present, eg FullscreenActivity. */
-    public static final int CONTROLS_POSITION_NONE = 2;
+    public static final int CONTROLS_POSITION_NONE = 1;
 
     /**
      * A listener that gets notified of changes to the fullscreen state.
@@ -224,9 +222,6 @@
             case CONTROLS_POSITION_TOP:
                 mTopControlContainerHeight = controlContainerHeight;
                 break;
-            case CONTROLS_POSITION_BOTTOM:
-                mBottomControlContainerHeight = controlContainerHeight;
-                break;
             case CONTROLS_POSITION_NONE:
                 // Treat the case of no controls as controls always being totally offscreen.
                 mControlOffsetRatio = 1.0f;
@@ -237,11 +232,6 @@
         updateControlOffset();
     }
 
-    @Override
-    public boolean areBrowserControlsAtBottom() {
-        return mControlsPosition == CONTROLS_POSITION_BOTTOM;
-    }
-
     /**
      * @return The visibility delegate that allows browser UI to control the browser control
      *         visibility.
@@ -415,17 +405,10 @@
     private void updateControlOffset() {
         if (mControlsPosition == CONTROLS_POSITION_NONE) return;
 
-        float topOffsetRatio = 0;
-
-        float rendererControlOffset;
-        if (mControlsPosition == CONTROLS_POSITION_BOTTOM) {
-            rendererControlOffset =
-                    Math.abs(mRendererBottomControlOffset / getBottomControlsHeight());
-        } else {
-            rendererControlOffset = Math.abs(mRendererTopControlOffset / getTopControlsHeight());
-        }
-
+        float rendererControlOffset = Math.abs(mRendererTopControlOffset / getTopControlsHeight());
         final boolean isNaNRendererControlOffset = Float.isNaN(rendererControlOffset);
+
+        float topOffsetRatio = 0;
         if (!isNaNRendererControlOffset) topOffsetRatio = rendererControlOffset;
         mControlOffsetRatio = topOffsetRatio;
     }
@@ -512,9 +495,7 @@
         TraceEvent.begin("FullscreenManager:updateVisuals");
 
         float offset = 0f;
-        if (mControlsPosition == CONTROLS_POSITION_BOTTOM) {
-            offset = getBottomControlOffset();
-        } else if (mControlsPosition == CONTROLS_POSITION_TOP) {
+        if (mControlsPosition == CONTROLS_POSITION_TOP) {
             offset = getTopControlOffset();
         }
 
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/fullscreen/FullscreenManager.java b/chrome/android/java/src/org/chromium/chrome/browser/fullscreen/FullscreenManager.java
index dde92c34..f68b1830 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/fullscreen/FullscreenManager.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/fullscreen/FullscreenManager.java
@@ -51,11 +51,6 @@
     public abstract int getTopControlsHeight();
 
     /**
-     * @return Whether or not the browser controls are attached to the bottom of the screen.
-     */
-    public abstract boolean areBrowserControlsAtBottom();
-
-    /**
      * @return The ratio that the browser controls are off screen; this will be a number [0,1]
      *         where 1 is completely hidden and 0 is completely shown.
      */
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/infobar/InfoBarContainer.java b/chrome/android/java/src/org/chromium/chrome/browser/infobar/InfoBarContainer.java
index d3ff7232..464050d 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/infobar/InfoBarContainer.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/infobar/InfoBarContainer.java
@@ -492,7 +492,7 @@
     protected boolean shouldConsumeScroll(int scrollOffsetY, int scrollExtentY) {
         ChromeFullscreenManager manager = mTab.getActivity().getFullscreenManager();
 
-        if (!manager.areBrowserControlsAtBottom()) return true;
+        if (manager.getBottomControlsHeight() <= 0) return true;
 
         boolean isScrollingDownward = scrollOffsetY > mLastScrollOffsetY;
         boolean didDirectionChange = isScrollingDownward != mIsScrollingDownward;
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/net/qualityprovider/ExternalEstimateProviderAndroid.java b/chrome/android/java/src/org/chromium/chrome/browser/net/qualityprovider/ExternalEstimateProviderAndroid.java
deleted file mode 100644
index f3c163c..0000000
--- a/chrome/android/java/src/org/chromium/chrome/browser/net/qualityprovider/ExternalEstimateProviderAndroid.java
+++ /dev/null
@@ -1,110 +0,0 @@
-// Copyright 2015 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-package org.chromium.chrome.browser.net.qualityprovider;
-
-import org.chromium.base.NonThreadSafe;
-import org.chromium.base.annotations.CalledByNative;
-import org.chromium.base.annotations.JNINamespace;
-import org.chromium.chrome.browser.AppHooks;
-
-/**
- * This class provides a base class implementation and may be overridden on operating systems that
- * provide more useful APIs. All method calls from native code will happen on the thread where
- * this object is constructed, but calls from subclasses (specifically,
- * {@link #notifyExternalEstimateProviderAndroidUpdate()} can happen on other threads.
- */
-@JNINamespace("chrome::android")
-public class ExternalEstimateProviderAndroid {
-    /**
-     * Value to return if a valid value is unavailable.
-     */
-    protected static final int NO_VALUE = -1;
-    private NonThreadSafe mThreadCheck = new NonThreadSafe();
-    private final Object mLock = new Object();
-
-    private long mNativePtr;
-
-    @CalledByNative
-    private static ExternalEstimateProviderAndroid create(long nativePtr) {
-        return AppHooks.get().createExternalEstimateProviderAndroid(nativePtr);
-    }
-
-    /**
-     * Creates an instance of {@link ExternalEstimateProviderAndroid}.
-     */
-    protected ExternalEstimateProviderAndroid(long nativePtr) {
-        mNativePtr = nativePtr;
-    }
-
-    @CalledByNative
-    private void destroy() {
-        synchronized (mLock) {
-            mNativePtr = 0;
-        }
-    }
-
-    /**
-     * Requests the provider to update the network quality estimate.
-     */
-    @CalledByNative
-    protected void requestUpdate() {
-        assert mThreadCheck.calledOnValidThread();
-    }
-
-    /**
-     * @return Expected RTT duration in milliseconds or {@link #NO_VALUE} if the estimate is
-     *         unavailable.
-     */
-    @CalledByNative
-    protected int getRTTMilliseconds() {
-        assert mThreadCheck.calledOnValidThread();
-        return NO_VALUE;
-    }
-
-    /**
-     * @return The expected downstream throughput in Kbps (Kilobits per second) or
-     *         {@link #NO_VALUE} if the estimate is unavailable.
-     */
-    @CalledByNative
-    protected long getDownstreamThroughputKbps() {
-        assert mThreadCheck.calledOnValidThread();
-        return NO_VALUE;
-    }
-
-    /**
-     * @return The expected upstream throughput in Kbps (Kilobits per second) or
-     *         {@link #NO_VALUE} if the estimate is unavailable.
-     */
-    protected long getUpstreamThroughputKbps() {
-        assert mThreadCheck.calledOnValidThread();
-        return NO_VALUE;
-    }
-
-    @CalledByNative
-    private static int getNoValue() {
-        return NO_VALUE;
-    }
-
-    /**
-     * Requests the provider to clear its cached network quality estimate.
-     */
-    @CalledByNative
-    protected void clearCachedEstimate() {
-        assert mThreadCheck.calledOnValidThread();
-    }
-
-    protected final void notifyExternalEstimateProviderAndroidUpdate() {
-        synchronized (mLock) {
-            if (mNativePtr == 0) return;
-
-            // It's important to call this inside the critical section, to ensure the native object
-            // isn't destroyed on its origin thread in the meantime.
-            nativeNotifyExternalEstimateProviderAndroidUpdate(mNativePtr);
-        }
-    }
-
-    private native void nativeNotifyExternalEstimateProviderAndroidUpdate(
-            long nativeExternalEstimateProviderAndroid);
-}
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/payments/AddressEditor.java b/chrome/android/java/src/org/chromium/chrome/browser/payments/AddressEditor.java
index 74fd395..c65b2e4 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/payments/AddressEditor.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/payments/AddressEditor.java
@@ -28,6 +28,7 @@
 import java.util.Locale;
 import java.util.Map;
 import java.util.Set;
+import java.util.UUID;
 
 import javax.annotation.Nullable;
 
@@ -58,17 +59,20 @@
     private EditorModel mEditor;
     private ProgressDialog mProgressDialog;
     private boolean mEmailFieldIncluded;
+    private boolean mSaveToDisk;
 
     /**
      * Builds an address editor.
      *
      * @param emailFieldIncluded True if the address editor has an email field. The autofill form
-     * has an email address, and the payment request doesn't.
+     *                           has an email address, and the payment request doesn't.
+     * @param saveToDisk         Whether to save changes to disk after editing.
      */
-    public AddressEditor(boolean emailFieldIncluded) {
+    public AddressEditor(boolean emailFieldIncluded, boolean saveToDisk) {
         mPhoneFormatter = new PhoneNumberUtil.CountryAwareFormatTextWatcher();
         mPhoneValidator = new CountryAwarePhoneNumberValidator();
         mEmailFieldIncluded = emailFieldIncluded;
+        mSaveToDisk = saveToDisk;
     }
 
     /**
@@ -278,7 +282,18 @@
         }
 
         // Save the edited autofill profile locally.
-        profile.setGUID(PersonalDataManager.getInstance().setProfileToLocal(mProfile));
+        if (mSaveToDisk) {
+            profile.setGUID(PersonalDataManager.getInstance().setProfileToLocal(mProfile));
+        }
+
+        if (profile.getGUID().isEmpty()) {
+            assert !mSaveToDisk;
+
+            // Set a fake guid for a new temp AutofillProfile to be used in CardEditor. Note that
+            // this temp AutofillProfile should not be saved to disk.
+            profile.setGUID(UUID.randomUUID().toString());
+        }
+
         profile.setIsLocal(true);
     }
 
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/payments/CardEditor.java b/chrome/android/java/src/org/chromium/chrome/browser/payments/CardEditor.java
index 95757ea..67d319ff 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/payments/CardEditor.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/payments/CardEditor.java
@@ -15,6 +15,7 @@
 import org.chromium.base.ApiCompatibilityUtils;
 import org.chromium.base.Callback;
 import org.chromium.chrome.R;
+import org.chromium.chrome.browser.ChromeActivity;
 import org.chromium.chrome.browser.autofill.CardType;
 import org.chromium.chrome.browser.autofill.CreditCardScanner;
 import org.chromium.chrome.browser.autofill.PersonalDataManager;
@@ -161,6 +162,7 @@
     private boolean mIsScanning;
     private int mCurrentMonth;
     private int mCurrentYear;
+    private boolean mIsIncognito;
 
     /**
      * Builds a credit card editor.
@@ -266,6 +268,10 @@
             }
         };
         mCalendar.execute();
+
+        ChromeActivity activity = ChromeActivity.fromWebContents(mWebContents);
+        mIsIncognito = activity != null && activity.getCurrentTabModel() != null
+                && activity.getCurrentTabModel().isIncognito();
     }
 
     private boolean isCardNumberLengthMaximum(@Nullable CharSequence value) {
@@ -337,7 +343,8 @@
      * [ name on card                        ]
      * [ expiration month ][ expiration year ]
      * [ billing address dropdown            ]
-     * [ save this card checkbox             ] <-- Shown only for new cards.
+     * [ save this card checkbox             ] <-- Shown only for new cards when not in incognito
+     *                                             mode.
      *
      * Server cards have the following fields instead.
      *
@@ -386,8 +393,8 @@
         // Always show the billing address dropdown.
         addBillingAddressDropdown(editor, card);
 
-        // Allow saving new cards on disk.
-        if (isNewCard) addSaveCardCheckbox(editor);
+        // Allow saving new cards on disk unless in incognito mode.
+        if (isNewCard && !mIsIncognito) addSaveCardCheckbox(editor);
 
         // If the user clicks [Cancel], send |toEdit| card back to the caller (will return original
         // state, which could be null, a full card, or a partial card).
@@ -778,7 +785,7 @@
     }
 
     /**
-     * Saves the edited credit card.
+     * Saves the edited credit card. Note that we do not save changes to disk in incognito mode.
      *
      * If this is a server card, then only its billing address identifier is updated.
      *
@@ -790,7 +797,7 @@
 
         PersonalDataManager pdm = PersonalDataManager.getInstance();
         if (!card.getIsLocal()) {
-            pdm.updateServerCardBillingAddress(card);
+            if (!mIsIncognito) pdm.updateServerCardBillingAddress(card);
             return;
         }
 
@@ -808,11 +815,12 @@
         card.setIssuerIconDrawableId(displayableCard.getIssuerIconDrawableId());
 
         if (!isNewCard) {
-            pdm.setCreditCard(card);
+            if (!mIsIncognito) pdm.setCreditCard(card);
             return;
         }
 
         if (mSaveCardCheckbox != null && mSaveCardCheckbox.isChecked()) {
+            assert !mIsIncognito;
             card.setGUID(pdm.setCreditCard(card));
         }
     }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/payments/ContactEditor.java b/chrome/android/java/src/org/chromium/chrome/browser/payments/ContactEditor.java
index 8e98658..18a78b4 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/payments/ContactEditor.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/payments/ContactEditor.java
@@ -19,6 +19,7 @@
 
 import java.util.HashSet;
 import java.util.Set;
+import java.util.UUID;
 
 import javax.annotation.Nullable;
 
@@ -39,6 +40,7 @@
     private final boolean mRequestPayerName;
     private final boolean mRequestPayerPhone;
     private final boolean mRequestPayerEmail;
+    private final boolean mSaveToDisk;
     private final Set<CharSequence> mPayerNames;
     private final Set<CharSequence> mPhoneNumbers;
     private final Set<CharSequence> mEmailAddresses;
@@ -48,16 +50,18 @@
     /**
      * Builds a contact information editor.
      *
-     * @param requestPayerName Whether to request the user's name.
+     * @param requestPayerName  Whether to request the user's name.
      * @param requestPayerPhone Whether to request the user's phone number.
      * @param requestPayerEmail Whether to request the user's email address.
+     * @param saveToDisk        Whether to save changes to disk.
      */
-    public ContactEditor(boolean requestPayerName,
-            boolean requestPayerPhone, boolean requestPayerEmail) {
+    public ContactEditor(boolean requestPayerName, boolean requestPayerPhone,
+            boolean requestPayerEmail, boolean saveToDisk) {
         assert requestPayerName || requestPayerPhone || requestPayerEmail;
         mRequestPayerName = requestPayerName;
         mRequestPayerPhone = requestPayerPhone;
         mRequestPayerEmail = requestPayerEmail;
+        mSaveToDisk = saveToDisk;
         mPayerNames = new HashSet<>();
         mPhoneNumbers = new HashSet<>();
         mEmailAddresses = new HashSet<>();
@@ -211,7 +215,17 @@
                 profile.setEmailAddress(email);
             }
 
-            profile.setGUID(PersonalDataManager.getInstance().setProfileToLocal(profile));
+            if (mSaveToDisk) {
+                profile.setGUID(PersonalDataManager.getInstance().setProfileToLocal(profile));
+            }
+
+            if (profile.getGUID().isEmpty()) {
+                assert !mSaveToDisk;
+
+                // Set a fake guid for a new temp AutofillProfile.
+                profile.setGUID(UUID.randomUUID().toString());
+            }
+
             profile.setIsLocal(true);
             contact.completeContact(profile.getGUID(), name, phone, email);
             callback.onResult(contact);
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/payments/PaymentRequestImpl.java b/chrome/android/java/src/org/chromium/chrome/browser/payments/PaymentRequestImpl.java
index 5b8e0f0..4f028298 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/payments/PaymentRequestImpl.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/payments/PaymentRequestImpl.java
@@ -389,13 +389,15 @@
 
         mApps = new ArrayList<>();
 
-        mAddressEditor = new AddressEditor(/*emailFieldIncluded=*/false);
-        mCardEditor = new CardEditor(mWebContents, mAddressEditor, sObserverForTest);
-
         ChromeActivity activity = ChromeActivity.fromWebContents(mWebContents);
         mIsIncognito = activity != null && activity.getCurrentTabModel() != null
                 && activity.getCurrentTabModel().isIncognito();
 
+        // Do not persist changes on disk in incognito mode.
+        mAddressEditor =
+                new AddressEditor(/*emailFieldIncluded=*/false, /*saveToDisk=*/!mIsIncognito);
+        mCardEditor = new CardEditor(mWebContents, mAddressEditor, sObserverForTest);
+
         mJourneyLogger = new JourneyLogger(mIsIncognito, mWebContents.getLastCommittedUrl());
 
         if (sCanMakePaymentQueries == null) sCanMakePaymentQueries = new ArrayMap<>();
@@ -525,8 +527,9 @@
         }
 
         if (mRequestPayerName || mRequestPayerPhone || mRequestPayerEmail) {
-            mContactEditor =
-                    new ContactEditor(mRequestPayerName, mRequestPayerPhone, mRequestPayerEmail);
+            // Do not persist changes on disk in incognito mode.
+            mContactEditor = new ContactEditor(mRequestPayerName, mRequestPayerPhone,
+                    mRequestPayerEmail, /*saveToDisk=*/!mIsIncognito);
             mContactSection = new ContactDetailsSection(activity,
                     Collections.unmodifiableList(profiles), mContactEditor, mJourneyLogger);
         }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/preferences/autofill/AutofillProfileEditorPreference.java b/chrome/android/java/src/org/chromium/chrome/browser/preferences/autofill/AutofillProfileEditorPreference.java
index 67498f5..6221bfa 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/preferences/autofill/AutofillProfileEditorPreference.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/preferences/autofill/AutofillProfileEditorPreference.java
@@ -48,7 +48,8 @@
     }
 
     private void prepareAddressEditor() {
-        AddressEditor addressEditor = new AddressEditor(/*emailFieldIncluded=*/true);
+        AddressEditor addressEditor =
+                new AddressEditor(/*emailFieldIncluded=*/true, /*saveToDisk=*/true);
         addressEditor.setEditorDialog(mEditorDialog);
 
         addressEditor.edit(mAutofillAddress, new Callback<AutofillAddress>() {
diff --git a/chrome/android/java_sources.gni b/chrome/android/java_sources.gni
index 13263cc..0d3c5b63 100644
--- a/chrome/android/java_sources.gni
+++ b/chrome/android/java_sources.gni
@@ -649,7 +649,6 @@
   "java/src/org/chromium/chrome/browser/mojo/ChromeInterfaceRegistrar.java",
   "java/src/org/chromium/chrome/browser/multiwindow/MultiInstanceChromeTabbedActivity.java",
   "java/src/org/chromium/chrome/browser/multiwindow/MultiWindowUtils.java",
-  "java/src/org/chromium/chrome/browser/net/qualityprovider/ExternalEstimateProviderAndroid.java",
   "java/src/org/chromium/chrome/browser/net/spdyproxy/DataReductionProxySettings.java",
   "java/src/org/chromium/chrome/browser/nfc/BeamCallback.java",
   "java/src/org/chromium/chrome/browser/nfc/BeamController.java",
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/browseractions/BrowserActionActivityTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/browseractions/BrowserActionActivityTest.java
index 247fcfe9..93cf361 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/browseractions/BrowserActionActivityTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/browseractions/BrowserActionActivityTest.java
@@ -10,8 +10,12 @@
 import android.app.Instrumentation.ActivityMonitor;
 import android.app.PendingIntent;
 import android.app.ProgressDialog;
+import android.content.ContentResolver;
 import android.content.Context;
 import android.content.Intent;
+import android.graphics.Bitmap;
+import android.graphics.BitmapFactory;
+import android.graphics.drawable.Drawable;
 import android.net.Uri;
 import android.os.Build;
 import android.os.Bundle;
@@ -19,6 +23,7 @@
 import android.support.annotation.DrawableRes;
 import android.support.customtabs.browseractions.BrowserActionItem;
 import android.support.customtabs.browseractions.BrowserActionsIntent;
+import android.support.customtabs.browseractions.BrowserServiceFileProvider;
 import android.support.test.InstrumentationRegistry;
 import android.support.test.filters.SmallTest;
 import android.util.Pair;
@@ -33,6 +38,7 @@
 
 import org.chromium.base.ActivityState;
 import org.chromium.base.ApplicationStatus;
+import org.chromium.base.Callback;
 import org.chromium.base.ContextUtils;
 import org.chromium.base.ThreadUtils;
 import org.chromium.base.test.util.CallbackHelper;
@@ -80,7 +86,9 @@
     private static final String CUSTOM_ITEM_TITLE_3 = "Custom item wit invalid icon id";
     private static final String CUSTOM_ITEM_TITLE_4 = "Custom item without icon";
     @DrawableRes
-    private static final int CUSTOM_ITEM_ICON_BITMAP_DRAWABLE_ID = R.drawable.star_green;
+    private static final int CUSTOM_ITEM_ICON_BITMAP_DRAWABLE_ID_1 = R.drawable.star_green;
+    @DrawableRes
+    private static final int CUSTOM_ITEM_ICON_BITMAP_DRAWABLE_ID_2 = R.drawable.star_gray;
     @DrawableRes
     private static final int CUSTOM_ITEM_ICON_VECTOR_DRAWABLE_ID = R.drawable.ic_add;
     @DrawableRes
@@ -173,6 +181,24 @@
         mTestServer.stopAndDestroyServer();
     }
 
+    private void assertDrawableNull(Context context, ContextMenuItem item) {
+        item.getDrawableAsync(context, new Callback<Drawable>() {
+            @Override
+            public void onResult(Drawable drawable) {
+                Assert.assertNull(drawable);
+            }
+        });
+    }
+
+    private void assertDrawableNotNull(Context context, ContextMenuItem item) {
+        item.getDrawableAsync(context, new Callback<Drawable>() {
+            @Override
+            public void onResult(Drawable drawable) {
+                Assert.assertNotNull(drawable);
+            }
+        });
+    }
+
     @Test
     @SmallTest
     /**
@@ -215,10 +241,10 @@
                             BrowserActionsContextMenuHelper.CUSTOM_BROWSER_ACTIONS_ID_GROUP.get(
                                     i)));
         }
-        Assert.assertNotNull(contextMenuItems.get(5).getDrawable(context));
-        Assert.assertNotNull(contextMenuItems.get(6).getDrawable(context));
-        Assert.assertNull(contextMenuItems.get(7).getDrawable(context));
-        Assert.assertNull(contextMenuItems.get(8).getDrawable(context));
+        assertDrawableNotNull(context, contextMenuItems.get(5));
+        assertDrawableNotNull(context, contextMenuItems.get(6));
+        assertDrawableNull(context, contextMenuItems.get(7));
+        assertDrawableNull(context, contextMenuItems.get(8));
     }
 
     @Test
@@ -259,10 +285,37 @@
                             BrowserActionsContextMenuHelper.CUSTOM_BROWSER_ACTIONS_ID_GROUP.get(
                                     i)));
         }
-        Assert.assertNotNull(contextMenuItems.get(2).getDrawable(context));
-        Assert.assertNotNull(contextMenuItems.get(3).getDrawable(context));
-        Assert.assertNull(contextMenuItems.get(4).getDrawable(context));
-        Assert.assertNull(contextMenuItems.get(5).getDrawable(context));
+        assertDrawableNotNull(context, contextMenuItems.get(2));
+        assertDrawableNotNull(context, contextMenuItems.get(3));
+        assertDrawableNull(context, contextMenuItems.get(4));
+        assertDrawableNull(context, contextMenuItems.get(5));
+    }
+
+    @Test
+    @SmallTest
+    public void testCustomIconShownFromProviderCorrectly() throws Exception {
+        List<BrowserActionItem> items = createCustomItemsWithProvider();
+        startBrowserActionActivity(mTestPage, items, 0);
+        mOnBrowserActionsMenuShownCallback.waitForCallback(0);
+
+        List<Pair<Integer, List<ContextMenuItem>>> menus = mItems;
+        List<ContextMenuItem> contextMenuItems = menus.get(0).second;
+        ContentResolver resolver = InstrumentationRegistry.getTargetContext().getContentResolver();
+
+        Assert.assertTrue(contextMenuItems.get(5) instanceof BrowserActionsCustomContextMenuItem);
+        BrowserActionsCustomContextMenuItem customItems =
+                (BrowserActionsCustomContextMenuItem) contextMenuItems.get(5);
+        CallbackHelper imageShownCallback = new CallbackHelper();
+        Context context = InstrumentationRegistry.getTargetContext();
+        Callback<Drawable> callback = new Callback<Drawable>() {
+            @Override
+            public void onResult(Drawable drawable) {
+                Assert.assertNotNull(drawable);
+                imageShownCallback.notifyCalled();
+            }
+        };
+        customItems.getDrawableAsync(context, callback);
+        imageShownCallback.waitForCallback(0);
     }
 
     @Test
@@ -683,10 +736,18 @@
         intent.putExtra(BrowserActionsIntent.EXTRA_APP_ID, pendingIntent);
 
         ArrayList<Bundle> customItemBundles = new ArrayList<>();
+        List<Uri> uris = new ArrayList<>();
         for (BrowserActionItem item : items) {
             Bundle customItemBundle = new Bundle();
             customItemBundle.putString(BrowserActionsIntent.KEY_TITLE, item.getTitle());
-            customItemBundle.putInt(BrowserActionsIntent.KEY_ICON_ID, item.getIconId());
+            if (item.getIconId() != 0) {
+                customItemBundle.putInt(BrowserActionsIntent.KEY_ICON_ID, item.getIconId());
+            }
+            if (item.getIconUri() != null) {
+                Uri uri = item.getIconUri();
+                customItemBundle.putParcelable(BrowserActionsIntent.KEY_ICON_URI, uri);
+                uris.add(uri);
+            }
             customItemBundle.putParcelable(BrowserActionsIntent.KEY_ACTION, item.getAction());
             customItemBundles.add(customItemBundle);
         }
@@ -695,6 +756,10 @@
         intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
 
         intent.setClass(context, BrowserActionActivity.class);
+        if (!uris.isEmpty()) {
+            BrowserServiceFileProvider.grantReadPermission(
+                    intent, uris, InstrumentationRegistry.getContext());
+        }
         // Android Test Rule auto adds {@link Intent.FLAG_ACTIVITY_NEW_TASK} which violates {@link
         // BrowserActionsIntent} policy. Add an extra to skip Intent.FLAG_ACTIVITY_NEW_TASK check
         // for test.
@@ -711,7 +776,7 @@
         List<BrowserActionItem> items = new ArrayList<>();
         PendingIntent action1 = createCustomItemAction(mTestPage);
         BrowserActionItem item1 = new BrowserActionItem(
-                CUSTOM_ITEM_TITLE_1, action1, CUSTOM_ITEM_ICON_BITMAP_DRAWABLE_ID);
+                CUSTOM_ITEM_TITLE_1, action1, CUSTOM_ITEM_ICON_BITMAP_DRAWABLE_ID_1);
         PendingIntent action2 = createCustomItemAction(mTestPage);
         BrowserActionItem item2 = new BrowserActionItem(
                 CUSTOM_ITEM_TITLE_2, action2, CUSTOM_ITEM_ICON_VECTOR_DRAWABLE_ID);
@@ -726,4 +791,17 @@
         items.add(item4);
         return items;
     }
+
+    private List<BrowserActionItem> createCustomItemsWithProvider() {
+        List<BrowserActionItem> items = new ArrayList<>();
+        PendingIntent action1 = createCustomItemAction(mTestPage);
+        Context context = InstrumentationRegistry.getTargetContext();
+        Bitmap bm = BitmapFactory.decodeResource(
+                context.getResources(), CUSTOM_ITEM_ICON_BITMAP_DRAWABLE_ID_1);
+
+        Uri uri = BrowserServiceFileProvider.generateUri(context, bm, CUSTOM_ITEM_TITLE_1, 1);
+        BrowserActionItem item = new BrowserActionItem(CUSTOM_ITEM_TITLE_1, action1, uri);
+        items.add(item);
+        return items;
+    }
 }
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/compositor/layouts/LayoutManagerTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/compositor/layouts/LayoutManagerTest.java
index 1bc2924b..e9ed617d 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/compositor/layouts/LayoutManagerTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/compositor/layouts/LayoutManagerTest.java
@@ -134,6 +134,7 @@
 
         mManagerPhone = new LayoutManagerChromePhone(layoutManagerHost);
         mManager = mManagerPhone;
+        mManager.getAnimationHandler().enableTestingMode();
         mManager.init(mTabModelSelector, null, null, container, null, null);
         initializeMotionEvent();
     }
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/payments/PaymentRequestContactDetailsSectionUnitTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/payments/PaymentRequestContactDetailsSectionUnitTest.java
index 132f0f64..ff54ac3 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/payments/PaymentRequestContactDetailsSectionUnitTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/payments/PaymentRequestContactDetailsSectionUnitTest.java
@@ -39,7 +39,8 @@
 
     private void createContactDetailsSectionWithProfiles(List<AutofillProfile> autofillProfiles,
             boolean requestPayerName, boolean requestPayerPhone, boolean requestPayerEmail) {
-        mContactEditor = new ContactEditor(requestPayerName, requestPayerPhone, requestPayerEmail);
+        mContactEditor = new ContactEditor(
+                requestPayerName, requestPayerPhone, requestPayerEmail, /*saveToDisk=*/true);
         mContactDetailsSection = new ContactDetailsSection(
                 InstrumentationRegistry.getTargetContext(), autofillProfiles, mContactEditor, null);
     }
diff --git a/chrome/android/junit/src/org/chromium/chrome/browser/customtabs/NavigationInfoCaptureTriggerTest.java b/chrome/android/junit/src/org/chromium/chrome/browser/customtabs/NavigationInfoCaptureTriggerTest.java
index d9365de..79329eb 100644
--- a/chrome/android/junit/src/org/chromium/chrome/browser/customtabs/NavigationInfoCaptureTriggerTest.java
+++ b/chrome/android/junit/src/org/chromium/chrome/browser/customtabs/NavigationInfoCaptureTriggerTest.java
@@ -122,6 +122,7 @@
      */
     @Test
     @Feature({"CustomTabs"})
+    @SuppressWarnings("unchecked")
     public void testResetOnNavigation() {
         testNormalFlow();
 
diff --git a/chrome/app/chrome_main.cc b/chrome/app/chrome_main.cc
index 4d8260b..af7b699 100644
--- a/chrome/app/chrome_main.cc
+++ b/chrome/app/chrome_main.cc
@@ -14,14 +14,8 @@
 #include "content/public/app/content_main.h"
 #include "content/public/common/content_switches.h"
 #include "headless/public/headless_shell.h"
-#include "ui/base/ui_base_switches.h"
-#include "ui/base/ui_features.h"
 #include "ui/gfx/switches.h"
 
-#if BUILDFLAG(ENABLE_MUS)
-#include "services/service_manager/runner/common/client_util.h"
-#endif
-
 #if defined(OS_MACOSX)
 #include "chrome/app/chrome_main_mac.h"
 #endif
@@ -49,39 +43,6 @@
 }
 #endif
 
-namespace {
-
-#if BUILDFLAG(ENABLE_MUS)
-void ConfigureMus(content::ContentMainParams* params) {
-  base::CommandLine* command_line = base::CommandLine::ForCurrentProcess();
-#if defined(OS_CHROMEOS)
-  // --mash implies --mus, so check it first.
-  if (command_line->HasSwitch(switches::kMash)) {
-    params->create_discardable_memory = false;
-    params->env_mode = aura::Env::Mode::MUS;
-    // Don't add --mus if the user had both --mash and --mus.
-    if (!command_line->HasSwitch(switches::kMus))
-      command_line->AppendSwitch(switches::kMus);
-    command_line->AppendSwitch(switches::kMusHostingViz);
-    return;
-  }
-#endif  // defined(OS_CHROMEOS)
-
-  // In config==mus the ui service runs in process and is shut down well before
-  // the rest of Chrome. Have Chrome create the DiscardableSharedMemoryManager
-  // to ensure the DiscardableSharedMemoryManager is destroyed later on. Doing
-  // this avoids lifetime issues when internal implementation details of
-  // DiscardableSharedMemoryManager assume DiscardableSharedMemoryManager is
-  // long lived.
-  if (command_line->HasSwitch(switches::kMus)) {
-    params->create_discardable_memory = true;
-    params->env_mode = aura::Env::Mode::MUS;
-  }
-}
-#endif  // BUILDFLAG(ENABLE_MUS)
-
-}  // namespace
-
 #if defined(OS_WIN)
 DLLEXPORT int __cdecl ChromeMain(HINSTANCE instance,
                                  sandbox::SandboxInterfaceInfo* sandbox_info,
@@ -137,10 +98,6 @@
   }
 #endif  // defined(OS_LINUX) || defined(OS_MACOSX) || defined(OS_WIN)
 
-#if BUILDFLAG(ENABLE_MUS)
-  ConfigureMus(&params);
-#endif
-
   int rv = content::ContentMain(params);
 
   return rv;
diff --git a/chrome/app/chromium_strings.grd b/chrome/app/chromium_strings.grd
index 3029058c..6b7e20cd 100644
--- a/chrome/app/chromium_strings.grd
+++ b/chrome/app/chromium_strings.grd
@@ -706,7 +706,7 @@
       </if>
 
       <!-- Enterprise sign-in dialog -->
-      <if expr="toolkit_views or is_macosx">
+      <if expr="toolkit_views">
         <message name="IDS_ENTERPRISE_SIGNIN_TITLE" desc="The title of the dialog to confirm linking the browser profile with the signed-in enterprise account">
           Link your Chromium data to this account?
         </message>
diff --git a/chrome/app/generated_resources.grd b/chrome/app/generated_resources.grd
index 369a97f..21b85148 100644
--- a/chrome/app/generated_resources.grd
+++ b/chrome/app/generated_resources.grd
@@ -8425,7 +8425,7 @@
       </if>
 
       <!-- Translate Bubble -->
-      <if expr="toolkit_views or is_macosx">
+      <if expr="toolkit_views">
         <message name="IDS_TRANSLATE_BUBBLE_BEFORE_TRANSLATE_TITLE" desc="Title text for the translate bubble when asking to translate a page.">
           Translate this page?
         </message>
@@ -10973,6 +10973,19 @@
         Move the controller to reposition. Click to finish.
       </message>
     </if>
+
+    <message name="IDS_CONFIRM_FILE_UPLOAD_TITLE" desc="Title of dialog for confirming that many files are about to be uploaded / given to the site. [ICU Syntax]">
+      {0, plural,
+        =1 {Upload one file to this site?}
+        other {Upload # files to this site?}
+      }
+    </message>
+    <message name="IDS_CONFIRM_FILE_UPLOAD_TEXT" desc="A message warning the user that they're about upload potentially many files to the site. This mentions where the files are from. The number of files are in the dialog title.">
+      This will upload all files from "<ph name="FOLDER_PATH">$1<ex>Desktop</ex></ph>". Only do this if you trust the site.
+    </message>
+    <message name="IDS_CONFIRM_FILE_UPLOAD_OK_BUTTON" desc="Button title confirming to upload multiple files to the site.">
+      Upload
+    </message>
   </messages>
  </release>
 </grit>
diff --git a/chrome/app/google_chrome_strings.grd b/chrome/app/google_chrome_strings.grd
index a9b6c321a..7b0104c 100644
--- a/chrome/app/google_chrome_strings.grd
+++ b/chrome/app/google_chrome_strings.grd
@@ -716,7 +716,7 @@
       </if>
 
       <!-- Enterprise sign-in dialog -->
-      <if expr="toolkit_views or is_macosx">
+      <if expr="toolkit_views">
         <message name="IDS_ENTERPRISE_SIGNIN_TITLE" desc="The title of the dialog to confirm linking the browser profile with the signed-in enterprise account">
           Link your Chrome data to this account?
         </message>
diff --git a/chrome/app/theme/theme_resources.grd b/chrome/app/theme/theme_resources.grd
index 89fef4f..3f6090c 100644
--- a/chrome/app/theme/theme_resources.grd
+++ b/chrome/app/theme/theme_resources.grd
@@ -47,7 +47,7 @@
         <structure type="chrome_scaled_image" name="IDR_APP_WINDOW_MINIMIZE_L" file="common/app_window_minimize_light.png" />
         <structure type="chrome_scaled_image" name="IDR_APP_WINDOW_MINIMIZE_P" file="common/app_window_minimize_active.png" />
       </if>
-      <if expr="toolkit_views or is_macosx">
+      <if expr="toolkit_views">
         <structure type="chrome_scaled_image" name="IDR_BACK" file="common/browser_back_normal.png" />
         <structure type="chrome_scaled_image" name="IDR_BACK_D" file="common/browser_back_disabled.png" />
         <structure type="chrome_scaled_image" name="IDR_BACK_H" file="common/browser_back_hover.png" />
@@ -145,7 +145,7 @@
         </if>
         <structure type="chrome_scaled_image" name="IDR_FIRST_RUN_COMPLETION" file="cros/first_run_completion.png" />
       </if>
-      <if expr="toolkit_views or is_macosx">
+      <if expr="toolkit_views">
         <structure type="chrome_scaled_image" name="IDR_FORWARD" file="common/browser_forward_normal.png" />
         <structure type="chrome_scaled_image" name="IDR_FORWARD_D" file="common/browser_forward_disabled.png" />
         <structure type="chrome_scaled_image" name="IDR_FORWARD_H" file="common/browser_forward_hover.png" />
@@ -270,7 +270,7 @@
       <structure type="chrome_scaled_image" name="IDR_PROFILE_AVATAR_25" file="common/profile_avatar_sun_cloud.png" />
       <structure type="chrome_scaled_image" name="IDR_PROFILE_AVATAR_26" file="common/profile_avatar_placeholder.png" />
       <structure type="chrome_scaled_image" name="IDR_PROFILE_AVATAR_PLACEHOLDER_LARGE" file="common/profile_avatar_placeholder_large.png" />
-      <structure type="chrome_scaled_image" name="IDR_PROFILES_TURN_ON_SYNC_ILLUSTRATION" file="common/turn_on_sync_illustration.png" />
+      <structure type="chrome_scaled_image" name="IDR_PROFILES_DICE_TURN_ON_SYNC" file="common/turn_on_sync_illustration.png" />
 
       <!-- New style avatar button -->
       <if expr="is_win">
@@ -363,7 +363,7 @@
         <structure type="chrome_scaled_image" name="IDR_AVATAR_NATIVE_BUTTON_PRESSED_TOP" file="win/avatar_button/win8/sign_in_button_pressed_top.png" />
         <structure type="chrome_scaled_image" name="IDR_AVATAR_NATIVE_BUTTON_PRESSED_TOP_LEFT" file="win/avatar_button/win8/sign_in_button_pressed_top_left.png" />
         <structure type="chrome_scaled_image" name="IDR_AVATAR_NATIVE_BUTTON_PRESSED_TOP_RIGHT" file="win/avatar_button/win8/sign_in_button_pressed_top_right.png" />
-      </if>  <!-- toolkit_views and not is_macosx -->
+      </if>
       <if expr="chromeos">
         <structure type="chrome_scaled_image" name="IDR_RESET_WARNING" file="cros/reset_warning.png" />
       </if>
diff --git a/chrome/browser/BUILD.gn b/chrome/browser/BUILD.gn
index 87f9b81..651e2cb 100644
--- a/chrome/browser/BUILD.gn
+++ b/chrome/browser/BUILD.gn
@@ -2072,8 +2072,6 @@
       "android/metrics/variations_session.cc",
       "android/mojo/chrome_interface_registrar_android.cc",
       "android/mojo/chrome_interface_registrar_android.h",
-      "android/net/external_estimate_provider_android.cc",
-      "android/net/external_estimate_provider_android.h",
       "android/ntp/android_content_suggestions_notifier.cc",
       "android/ntp/android_content_suggestions_notifier.h",
       "android/ntp/content_suggestions_notifier.cc",
@@ -4330,7 +4328,6 @@
       "../android/java/src/org/chromium/chrome/browser/metrics/UmaUtils.java",
       "../android/java/src/org/chromium/chrome/browser/metrics/VariationsSession.java",
       "../android/java/src/org/chromium/chrome/browser/mojo/ChromeInterfaceRegistrar.java",
-      "../android/java/src/org/chromium/chrome/browser/net/qualityprovider/ExternalEstimateProviderAndroid.java",
       "../android/java/src/org/chromium/chrome/browser/net/spdyproxy/DataReductionProxySettings.java",
       "../android/java/src/org/chromium/chrome/browser/notifications/ActionInfo.java",
       "../android/java/src/org/chromium/chrome/browser/notifications/NotificationPlatformBridge.java",
diff --git a/chrome/browser/DEPS b/chrome/browser/DEPS
index 21b62df..4a2df16 100644
--- a/chrome/browser/DEPS
+++ b/chrome/browser/DEPS
@@ -81,9 +81,8 @@
   "+third_party/libaom/av1_features.h",
   "+third_party/metrics_proto",
 
-  # Code under //ash runs out-of-process under mustash (chrome --mash) so it
-  # must be accessed via mojo interfaces in //ash/public/interfaces. See
-  # //ash/README.md.
+  # Code under //ash runs out-of-process in mash so it must be accessed via mojo
+  # interfaces in //ash/public/interfaces. See //ash/README.md.
   "-ash",
   "+ash/public",
   "+ash/components/quick_launch/public/mojom",
diff --git a/chrome/browser/OWNERS b/chrome/browser/OWNERS
index df2c5041..db9323f1 100644
--- a/chrome/browser/OWNERS
+++ b/chrome/browser/OWNERS
@@ -67,7 +67,6 @@
 
 per-file exo_parts.*=reveman@chromium.org
 
-per-file io_thread*=cbentzel@chromium.org
 per-file io_thread*=mmenke@chromium.org
 per-file io_thread*=rch@chromium.org
 per-file io_thread*=rsleevi@chromium.org
diff --git a/chrome/browser/about_flags.cc b/chrome/browser/about_flags.cc
index 7fd70f5..6e6b5ef 100644
--- a/chrome/browser/about_flags.cc
+++ b/chrome/browser/about_flags.cc
@@ -1506,6 +1506,14 @@
         ENABLE_DISABLE_VALUE_TYPE(switches::kEnablePinch,
                                   switches::kDisablePinch),
     },
+    {"enable_unified_multidevice_settings",
+     flag_descriptions::kEnableUnifiedMultiDeviceSettingsName,
+     flag_descriptions::kEnableUnifiedMultiDeviceSettingsDescription, kOsCrOS,
+     FEATURE_VALUE_TYPE(features::kEnableUnifiedMultiDeviceSettings)},
+    {"enable_unified_multidevice_setup",
+     flag_descriptions::kEnableUnifiedMultiDeviceSetupName,
+     flag_descriptions::kEnableUnifiedMultiDeviceSetupDescription, kOsCrOS,
+     FEATURE_VALUE_TYPE(features::kEnableUnifiedMultiDeviceSetup)},
     {"enable-video-player-chromecast-support",
      flag_descriptions::kVideoPlayerChromecastSupportName,
      flag_descriptions::kVideoPlayerChromecastSupportDescription, kOsCrOS,
@@ -1522,10 +1530,10 @@
      FEATURE_VALUE_TYPE(features::kMultiDeviceApi)},
     {"mus", flag_descriptions::kUseMusName,
      flag_descriptions::kUseMusDescription, kOsCrOS,
-     SINGLE_VALUE_TYPE(switches::kMus)},
+     FEATURE_VALUE_TYPE(features::kMus)},
     {"mash", flag_descriptions::kUseMashName,
      flag_descriptions::kUseMashDescription, kOsCrOS,
-     SINGLE_VALUE_TYPE(switches::kMash)},
+     FEATURE_VALUE_TYPE(features::kMash)},
     {"show-taps", flag_descriptions::kShowTapsName,
      flag_descriptions::kShowTapsDescription, kOsCrOS,
      SINGLE_VALUE_TYPE(ash::switches::kShowTaps)},
@@ -3784,7 +3792,7 @@
 bool SkipConditionalFeatureEntry(const FeatureEntry& entry) {
   version_info::Channel channel = chrome::GetChannel();
 #if defined(OS_CHROMEOS)
-  // Don't expose --mash on stable channel.
+  // Don't expose mash on stable channel.
   if (!strcmp("mash", entry.internal_name) &&
       channel == version_info::Channel::STABLE) {
     return true;
diff --git a/chrome/browser/android/compositor/tab_content_manager.cc b/chrome/browser/android/compositor/tab_content_manager.cc
index 52b68456..c7003b2 100644
--- a/chrome/browser/android/compositor/tab_content_manager.cc
+++ b/chrome/browser/android/compositor/tab_content_manager.cc
@@ -29,6 +29,7 @@
 #include "content/public/browser/web_contents.h"
 #include "jni/TabContentManager_jni.h"
 #include "ui/android/resources/ui_resource_provider.h"
+#include "ui/android/view_android.h"
 #include "ui/gfx/android/java_bitmap.h"
 #include "ui/gfx/geometry/dip_util.h"
 #include "ui/gfx/geometry/rect.h"
@@ -61,9 +62,14 @@
                    weak_factory_.GetWeakPtr());
 
     SkColorType color_type = kN32_SkColorType;
-    gfx::Size view_size_on_screen = rwhv->GetViewBounds().size();
+    gfx::Size view_size_in_pixels =
+        rwhv->GetNativeView()->GetPhysicalBackingSize();
+    if (view_size_in_pixels.IsEmpty()) {
+      result_callback.Run(SkBitmap(), content::READBACK_SURFACE_UNAVAILABLE);
+      return;
+    }
     gfx::Size thumbnail_size(
-        gfx::ScaleToCeiledSize(view_size_on_screen, thumbnail_scale_));
+        gfx::ScaleToCeiledSize(view_size_in_pixels, thumbnail_scale_));
     rwhv->CopyFromSurface(gfx::Rect(), thumbnail_size, result_callback,
                           color_type);
   }
diff --git a/chrome/browser/android/net/external_estimate_provider_android.cc b/chrome/browser/android/net/external_estimate_provider_android.cc
deleted file mode 100644
index 27b3933..0000000
--- a/chrome/browser/android/net/external_estimate_provider_android.cc
+++ /dev/null
@@ -1,138 +0,0 @@
-// Copyright 2015 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "chrome/browser/android/net/external_estimate_provider_android.h"
-
-#include <stdint.h>
-
-#include "base/location.h"
-#include "base/single_thread_task_runner.h"
-#include "content/public/browser/browser_thread.h"
-#include "jni/ExternalEstimateProviderAndroid_jni.h"
-
-using base::android::JavaParamRef;
-
-namespace chrome {
-namespace android {
-
-ExternalEstimateProviderAndroid::ExternalEstimateProviderAndroid()
-    : task_runner_(nullptr),
-      delegate_(nullptr),
-      weak_factory_(this) {
-  DCHECK(j_external_estimate_provider_.is_null());
-  DCHECK_GE(-1, no_value_);
-
-  if (!base::ThreadTaskRunnerHandle::IsSet())
-    return;
-
-  task_runner_ = base::ThreadTaskRunnerHandle::Get();
-  task_runner_->PostDelayedTask(
-      FROM_HERE,
-      base::Bind(&ExternalEstimateProviderAndroid::CreateJavaObject,
-                 weak_factory_.GetWeakPtr()),
-      base::TimeDelta::FromSeconds(10));
-}
-
-ExternalEstimateProviderAndroid::~ExternalEstimateProviderAndroid() {
-  DCHECK(thread_checker_.CalledOnValidThread());
-  if (!j_external_estimate_provider_.is_null()) {
-    Java_ExternalEstimateProviderAndroid_destroy(
-        base::android::AttachCurrentThread(), j_external_estimate_provider_);
-  }
-}
-
-void ExternalEstimateProviderAndroid::CreateJavaObject() {
-  DCHECK(thread_checker_.CalledOnValidThread());
-  DCHECK(j_external_estimate_provider_.is_null());
-
-  JNIEnv* env = base::android::AttachCurrentThread();
-  j_external_estimate_provider_.Reset(
-      Java_ExternalEstimateProviderAndroid_create(
-          env, reinterpret_cast<intptr_t>(this)));
-  DCHECK(!j_external_estimate_provider_.is_null());
-  DCHECK_EQ(no_value_, Java_ExternalEstimateProviderAndroid_getNoValue(env));
-  Update();
-}
-
-base::TimeDelta ExternalEstimateProviderAndroid::GetRTT() const {
-  DCHECK(thread_checker_.CalledOnValidThread());
-  DCHECK(!j_external_estimate_provider_.is_null());
-
-  JNIEnv* env = base::android::AttachCurrentThread();
-  int32_t milliseconds =
-      Java_ExternalEstimateProviderAndroid_getRTTMilliseconds(
-          env, j_external_estimate_provider_);
-  DCHECK_GE(milliseconds, no_value_);
-  return base::TimeDelta::FromMilliseconds(milliseconds);
-}
-
-int32_t ExternalEstimateProviderAndroid::GetDownstreamThroughputKbps() const {
-  DCHECK(thread_checker_.CalledOnValidThread());
-  DCHECK(!j_external_estimate_provider_.is_null());
-
-  JNIEnv* env = base::android::AttachCurrentThread();
-  int32_t kbps =
-      Java_ExternalEstimateProviderAndroid_getDownstreamThroughputKbps(
-          env, j_external_estimate_provider_);
-  DCHECK_GE(kbps, no_value_);
-  return kbps;
-}
-
-void ExternalEstimateProviderAndroid::SetUpdatedEstimateDelegate(
-    net::ExternalEstimateProvider::UpdatedEstimateDelegate* delegate) {
-  DCHECK(thread_checker_.CalledOnValidThread());
-  delegate_ = delegate;
-}
-
-void ExternalEstimateProviderAndroid::Update() const {
-  DCHECK(thread_checker_.CalledOnValidThread());
-  if (j_external_estimate_provider_.is_null())
-    return;
-  JNIEnv* env = base::android::AttachCurrentThread();
-  Java_ExternalEstimateProviderAndroid_requestUpdate(
-      env, j_external_estimate_provider_);
-}
-
-void ExternalEstimateProviderAndroid::
-    NotifyExternalEstimateProviderAndroidUpdate(
-        JNIEnv* env,
-        const JavaParamRef<jobject>& obj) {
-  if (!task_runner_)
-    return;
-
-  // Note that creating a weak pointer is safe even on a background thread,
-  // because this method is called from the same critical section as the Java
-  // destroy() method (so this object can't be destroyed while we're in this
-  // method), and the factory itself isn't bound to a thread, just the weak
-  // pointers are (but they are bound to the thread they're *dereferenced* on,
-  // which is the task runner thread). Once we are outside of the critical
-  // section, the weak pointer will be invalidated if the object is destroyed.
-  task_runner_->PostTask(
-      FROM_HERE,
-      base::Bind(
-          &ExternalEstimateProviderAndroid::NotifyUpdatedEstimateAvailable,
-          weak_factory_.GetWeakPtr()));
-}
-
-void ExternalEstimateProviderAndroid::NotifyUpdatedEstimateAvailable() const {
-  DCHECK(thread_checker_.CalledOnValidThread());
-
-  if (delegate_) {
-    delegate_->OnUpdatedEstimateAvailable(GetRTT(),
-                                          GetDownstreamThroughputKbps());
-  }
-}
-
-void ExternalEstimateProviderAndroid::ClearCachedEstimate() {
-  DCHECK(thread_checker_.CalledOnValidThread());
-  if (j_external_estimate_provider_.is_null())
-    return;
-
-  JNIEnv* env = base::android::AttachCurrentThread();
-  Java_ExternalEstimateProviderAndroid_clearCachedEstimate(
-      env, j_external_estimate_provider_);
-}
-
-}  // namespace android
-}  // namespace chrome
diff --git a/chrome/browser/android/net/external_estimate_provider_android.h b/chrome/browser/android/net/external_estimate_provider_android.h
deleted file mode 100644
index ad93bf1e..0000000
--- a/chrome/browser/android/net/external_estimate_provider_android.h
+++ /dev/null
@@ -1,88 +0,0 @@
-// Copyright 2015 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef CHROME_BROWSER_ANDROID_NET_EXTERNAL_ESTIMATE_PROVIDER_ANDROID_H_
-#define CHROME_BROWSER_ANDROID_NET_EXTERNAL_ESTIMATE_PROVIDER_ANDROID_H_
-
-#include <jni.h>
-#include <stdint.h>
-
-#include "base/android/scoped_java_ref.h"
-#include "base/macros.h"
-#include "base/memory/ref_counted.h"
-#include "base/memory/weak_ptr.h"
-#include "base/threading/thread_checker.h"
-#include "base/threading/thread_task_runner_handle.h"
-#include "base/time/time.h"
-#include "net/nqe/external_estimate_provider.h"
-
-namespace chrome {
-namespace android {
-
-// Native class that calls Java code exposed by
-// ExternalEstimateProviderAndroidHelper.java. Provides network quality
-// estimates as provided by Android. Estimates are automatically updated on a
-// network change event.
-class ExternalEstimateProviderAndroid : public net::ExternalEstimateProvider {
- public:
-  // Constructs and initializes the underlying provider.
-  ExternalEstimateProviderAndroid();
-
-  ~ExternalEstimateProviderAndroid() override;
-
-  void SetUpdatedEstimateDelegate(
-      net::ExternalEstimateProvider::UpdatedEstimateDelegate* delegate)
-      override;
-  void Update() const override;
-
-  // Called by Java when the external estimate provider has an updated value.
-  // This may be called on a thread different from |task_runner_|.
-  void NotifyExternalEstimateProviderAndroidUpdate(
-      JNIEnv* env,
-      const base::android::JavaParamRef<jobject>& obj);
-
- protected:
-  // Notifies the delegate that a new update to external estimate is available.
-  // Protected for testing.
-  void NotifyUpdatedEstimateAvailable() const;
-
-  // Returns the estimated RTT value. If the estimate is unavailable, a negative
-  // value is returned. Protected for testing.
-  virtual base::TimeDelta GetRTT() const;
-
-  // Returns the estimated downstream throughput (in Kbps -- Kilobits
-  // per second) is available.  If the estimate is unavailable, a negative value
-  // is returned. Protected for testing.
-  virtual int32_t GetDownstreamThroughputKbps() const;
-
- private:
-  // Creates the corresponding Java object.
-  void CreateJavaObject();
-
-  // net::ExternalEstimateProvider:
-  void ClearCachedEstimate() override;
-
-  // Value returned if valid value is unavailable.
-  const int32_t no_value_ = -1;
-
-  base::android::ScopedJavaGlobalRef<jobject> j_external_estimate_provider_;
-
-  // Used to post tasks back to this object's origin thread.
-  scoped_refptr<base::SingleThreadTaskRunner> task_runner_;
-
-  // Notified every time there is an update available from the network quality
-  // provider.
-  net::ExternalEstimateProvider::UpdatedEstimateDelegate* delegate_;
-
-  base::ThreadChecker thread_checker_;
-
-  base::WeakPtrFactory<ExternalEstimateProviderAndroid> weak_factory_;
-
-  DISALLOW_COPY_AND_ASSIGN(ExternalEstimateProviderAndroid);
-};
-
-}  // namespace android
-}  // namespace chrome
-
-#endif  // CHROME_BROWSER_ANDROID_NET_EXTERNAL_ESTIMATE_PROVIDER_ANDROID_H_
diff --git a/chrome/browser/android/vr_shell/vr_controller.cc b/chrome/browser/android/vr_shell/vr_controller.cc
index 5c0e5ea..cd9f30d 100644
--- a/chrome/browser/android/vr_shell/vr_controller.cc
+++ b/chrome/browser/android/vr_shell/vr_controller.cc
@@ -188,6 +188,10 @@
              : PlatformController::kLeftHanded;
 }
 
+bool VrController::GetRecentered() const {
+  return controller_state_->GetRecentered();
+}
+
 gfx::Quaternion VrController::Orientation() const {
   const gvr::Quatf& orientation = controller_state_->GetOrientation();
   return gfx::Quaternion(orientation.qx, orientation.qy, orientation.qz,
diff --git a/chrome/browser/android/vr_shell/vr_controller.h b/chrome/browser/android/vr_shell/vr_controller.h
index 59f1aa0..7d3da38 100644
--- a/chrome/browser/android/vr_shell/vr_controller.h
+++ b/chrome/browser/android/vr_shell/vr_controller.h
@@ -84,6 +84,7 @@
   base::TimeTicks GetLastTouchTimestamp() const override;
   base::TimeTicks GetLastButtonTimestamp() const override;
   PlatformController::Handedness GetHandedness() const override;
+  bool GetRecentered() const override;
 
  private:
   enum GestureDetectorState {
diff --git a/chrome/browser/android/vr_shell/vr_shell_gl.cc b/chrome/browser/android/vr_shell/vr_shell_gl.cc
index 29db29f..9d81263 100644
--- a/chrome/browser/android/vr_shell/vr_shell_gl.cc
+++ b/chrome/browser/android/vr_shell/vr_shell_gl.cc
@@ -734,6 +734,7 @@
   controller_model.laser_direction = controller_direction;
   controller_model.laser_origin = laser_origin;
   controller_model.handedness = controller_->GetHandedness();
+  controller_model.recentered = controller_->GetRecentered();
   controller_model_ = controller_model;
 
   ReticleModel reticle_model;
diff --git a/chrome/browser/autofill/autofill_provider_browsertest.cc b/chrome/browser/autofill/autofill_provider_browsertest.cc
index c86b1306..eed81e8 100644
--- a/chrome/browser/autofill/autofill_provider_browsertest.cc
+++ b/chrome/browser/autofill/autofill_provider_browsertest.cc
@@ -8,9 +8,12 @@
 #include "chrome/common/url_constants.h"
 #include "chrome/test/base/in_process_browser_test.h"
 #include "chrome/test/base/ui_test_utils.h"
+#include "components/autofill/content/browser/content_autofill_driver.h"
 #include "components/autofill/content/browser/content_autofill_driver_factory.h"
 #include "components/autofill/core/browser/test_autofill_client.h"
 #include "components/autofill/core/browser/test_autofill_provider.h"
+#include "components/autofill/core/common/autofill_features.h"
+#include "components/autofill/core/common/form_data.h"
 #include "components/autofill/core/common/submission_source.h"
 #include "content/public/browser/web_contents.h"
 #include "content/public/common/url_constants.h"
@@ -22,6 +25,7 @@
 #include "testing/gtest/include/gtest/gtest.h"
 
 using ::testing::_;
+using ::testing::Invoke;
 
 namespace autofill {
 namespace {
@@ -40,6 +44,42 @@
                     bool,
                     SubmissionSource,
                     base::TimeTicks));
+
+  MOCK_METHOD5(OnQueryFormFieldAutofill,
+               void(AutofillHandlerProxy* handler,
+                    int32_t id,
+                    const FormData& form,
+                    const FormFieldData& field,
+                    const gfx::RectF& bounding_box));
+
+  void OnQueryFormFieldAutofillImpl(AutofillHandlerProxy* handler,
+                                    int32_t id,
+                                    const FormData& form,
+                                    const FormFieldData& field,
+                                    const gfx::RectF& bounding_box) {
+    queried_form_ = form;
+    is_queried_ = true;
+  }
+
+  bool OnFormSubmittedImpl(AutofillHandlerProxy*,
+                           const FormData& form,
+                           bool success,
+                           SubmissionSource source,
+                           base::TimeTicks timestamp) {
+    submitted_form_ = form;
+    return false;
+  }
+
+  const FormData& queried_form() { return queried_form_; }
+
+  const FormData& submitted_form() { return submitted_form_; }
+
+  bool is_queried() { return is_queried_; }
+
+ private:
+  bool is_queried_ = false;
+  FormData queried_form_;
+  FormData submitted_form_;
 };
 
 }  // namespace
@@ -50,6 +90,14 @@
   ~AutofillProviderBrowserTest() override {}
 
   void SetUpOnMainThread() override {
+    autofill_provider_ = std::make_unique<MockAutofillProvider>();
+    embedded_test_server()->AddDefaultHandlers(base::FilePath(kDocRoot));
+    // Serve both a.com and b.com (and any other domain).
+    host_resolver()->AddRule("*", "127.0.0.1");
+    ASSERT_TRUE(embedded_test_server()->Start());
+  }
+
+  void CreateContentAutofillDriverFactoryForSubFrame() {
     content::WebContents* web_contents = WebContents();
     ASSERT_TRUE(web_contents != NULL);
 
@@ -57,19 +105,25 @@
         ContentAutofillDriverFactory::
             kContentAutofillDriverFactoryWebContentsUserDataKey);
 
+    // Replace the ContentAutofillDriverFactory for sub frame.
     ContentAutofillDriverFactory::CreateForWebContentsAndDelegate(
         web_contents, &autofill_client_, "en-US",
         AutofillManager::DISABLE_AUTOFILL_DOWNLOAD_MANAGER,
-        &autofill_provider_);
+        autofill_provider_.get());
+  }
 
-    embedded_test_server()->AddDefaultHandlers(base::FilePath(kDocRoot));
-    // Serve both a.com and b.com (and any other domain).
-    host_resolver()->AddRule("*", "127.0.0.1");
-    ASSERT_TRUE(embedded_test_server()->Start());
+  void ReplaceAutofillDriver() {
+    content::WebContents* web_contents = WebContents();
+    // Set AutofillProvider for current WebContents.
+    ContentAutofillDriverFactory* factory =
+        ContentAutofillDriverFactory::FromWebContents(web_contents);
+    ContentAutofillDriver* driver =
+        factory->DriverForFrame(web_contents->GetMainFrame());
+    driver->SetAutofillProviderForTesting(autofill_provider_.get());
   }
 
   void TearDownOnMainThread() override {
-    testing::Mock::VerifyAndClearExpectations(&autofill_provider_);
+    testing::Mock::VerifyAndClearExpectations(autofill_provider_.get());
   }
 
   content::RenderViewHost* RenderViewHost() {
@@ -100,8 +154,58 @@
                               false, false);
   }
 
+  void SetLabelChangeExpectationAndTriggerQuery() {
+    ReplaceAutofillDriver();
+
+    EXPECT_CALL(*autofill_provider_, OnQueryFormFieldAutofill(_, _, _, _, _))
+        .WillOnce(Invoke(autofill_provider_.get(),
+                         &MockAutofillProvider::OnQueryFormFieldAutofillImpl));
+
+    EXPECT_CALL(*autofill_provider_, OnFormSubmitted(_, _, _, _, _))
+        .WillOnce(Invoke(autofill_provider_.get(),
+                         &MockAutofillProvider::OnFormSubmittedImpl));
+
+    ui_test_utils::NavigateToURL(browser(), embedded_test_server()->GetURL(
+                                                "/autofill/label_change.html"));
+
+    std::string focus("document.getElementById('address').focus();");
+    ASSERT_TRUE(content::ExecuteScript(RenderViewHost(), focus));
+
+    SimulateUserTypingInFocuedField();
+    while (!autofill_provider_->is_queried()) {
+      base::RunLoop().RunUntilIdle();
+    }
+  }
+
+  void ChangeLabelAndCheckResult(const std::string& element_id,
+                                 bool expect_forms_same) {
+    std::string change_label_and_submit =
+        "document.getElementById('" + element_id +
+        "').innerHTML='address change';"
+        "document.getElementById('submit_button').click();";
+
+    ASSERT_TRUE(
+        content::ExecuteScript(RenderViewHost(), change_label_and_submit));
+    // Need to pay attention for a message that XHR has finished since there
+    // is no navigation to wait for.
+    content::DOMMessageQueue message_queue;
+    std::string message;
+    while (message_queue.WaitForMessage(&message)) {
+      if (message == "\"SUBMISSION_FINISHED\"")
+        break;
+    }
+
+    EXPECT_EQ("\"SUBMISSION_FINISHED\"", message);
+    EXPECT_EQ(2u, autofill_provider_->queried_form().fields.size());
+    EXPECT_EQ(2u, autofill_provider_->submitted_form().fields.size());
+    EXPECT_EQ(expect_forms_same,
+              autofill_provider_->submitted_form().SimilarFormAs(
+                  autofill_provider_->queried_form()));
+  }
+
  protected:
-  MockAutofillProvider autofill_provider_;
+  std::unique_ptr<MockAutofillProvider> autofill_provider_;
+  base::test::ScopedFeatureList scoped_feature_list_;
 
  private:
   TestAutofillClient autofill_client_;
@@ -109,7 +213,8 @@
 
 IN_PROC_BROWSER_TEST_F(AutofillProviderBrowserTest,
                        FrameDetachedOnFormlessSubmission) {
-  EXPECT_CALL(autofill_provider_,
+  CreateContentAutofillDriverFactoryForSubFrame();
+  EXPECT_CALL(*autofill_provider_,
               OnFormSubmitted(_, _, _, SubmissionSource::FRAME_DETACHED, _))
       .Times(1);
   ui_test_utils::NavigateToURL(
@@ -143,7 +248,8 @@
 
 IN_PROC_BROWSER_TEST_F(AutofillProviderBrowserTest,
                        FrameDetachedOnFormSubmission) {
-  EXPECT_CALL(autofill_provider_,
+  CreateContentAutofillDriverFactoryForSubFrame();
+  EXPECT_CALL(*autofill_provider_,
               OnFormSubmitted(_, _, _, SubmissionSource::FORM_SUBMISSION, _))
       .Times(1);
   ui_test_utils::NavigateToURL(
@@ -175,4 +281,36 @@
   EXPECT_EQ("\"SUBMISSION_FINISHED\"", message);
 }
 
+IN_PROC_BROWSER_TEST_F(AutofillProviderBrowserTest,
+                       LabelTagChangeImpactFormComparingWithFlagOn) {
+  scoped_feature_list_.InitAndEnableFeature(
+      features::kAutofillSkipComparingInferredLabels);
+  SetLabelChangeExpectationAndTriggerQuery();
+  ChangeLabelAndCheckResult("label_id", false /*expect_forms_same*/);
+}
+
+IN_PROC_BROWSER_TEST_F(AutofillProviderBrowserTest,
+                       InferredLabelChangeNotImpactFormComparingWithFlagOn) {
+  scoped_feature_list_.InitAndEnableFeature(
+      features::kAutofillSkipComparingInferredLabels);
+  SetLabelChangeExpectationAndTriggerQuery();
+  ChangeLabelAndCheckResult("p_id", true /*expect_forms_same*/);
+}
+
+IN_PROC_BROWSER_TEST_F(AutofillProviderBrowserTest,
+                       LabelTagChangeImpactFormComparingWithFlagOff) {
+  scoped_feature_list_.InitAndDisableFeature(
+      features::kAutofillSkipComparingInferredLabels);
+  SetLabelChangeExpectationAndTriggerQuery();
+  ChangeLabelAndCheckResult("label_id", false /*expect_forms_same*/);
+}
+
+IN_PROC_BROWSER_TEST_F(AutofillProviderBrowserTest,
+                       InferredLabelChangeImpactFormComparingWithFlagOff) {
+  scoped_feature_list_.InitAndDisableFeature(
+      features::kAutofillSkipComparingInferredLabels);
+  SetLabelChangeExpectationAndTriggerQuery();
+  ChangeLabelAndCheckResult("p_id", false /*expect_forms_same*/);
+}
+
 }  // namespace autofill
diff --git a/chrome/browser/browsing_data/chrome_browsing_data_remover_delegate.cc b/chrome/browser/browsing_data/chrome_browsing_data_remover_delegate.cc
index 6dc210e..d33e801 100644
--- a/chrome/browser/browsing_data/chrome_browsing_data_remover_delegate.cc
+++ b/chrome/browser/browsing_data/chrome_browsing_data_remover_delegate.cc
@@ -56,7 +56,6 @@
 #include "components/autofill/core/browser/personal_data_manager.h"
 #include "components/autofill/core/browser/webdata/autofill_webdata_service.h"
 #include "components/bookmarks/browser/bookmark_model.h"
-#include "components/browsing_data/core/features.h"
 #include "components/content_settings/core/browser/content_settings_registry.h"
 #include "components/content_settings/core/browser/host_content_settings_map.h"
 #include "components/content_settings/core/common/content_settings.h"
@@ -564,15 +563,9 @@
           prerender::PrerenderManager::CLEAR_PRERENDER_HISTORY);
     }
 
-    // When this feature is enabled, recent tabs and sessions will be deleted
-    // by NavigationEntryRemover and not here.
-    bool is_navigation_entry_remover_enabled = base::FeatureList::IsEnabled(
-        browsing_data::features::kRemoveNavigationHistory);
-
     // If the caller is removing history for all hosts, then clear ancillary
     // historical information.
-    if (!is_navigation_entry_remover_enabled &&
-        filter_builder.GetMode() == BrowsingDataFilterBuilder::BLACKLIST) {
+    if (filter_builder.GetMode() == BrowsingDataFilterBuilder::BLACKLIST) {
       // We also delete the list of recently closed tabs. Since these expire,
       // they can't be more than a day old, so we can simply clear them all.
       sessions::TabRestoreService* tab_service =
diff --git a/chrome/browser/browsing_data/navigation_entry_remover.cc b/chrome/browser/browsing_data/navigation_entry_remover.cc
index daa5613c..2d15678 100644
--- a/chrome/browser/browsing_data/navigation_entry_remover.cc
+++ b/chrome/browser/browsing_data/navigation_entry_remover.cc
@@ -6,12 +6,7 @@
 
 #include "build/build_config.h"
 #include "chrome/browser/profiles/profile.h"
-#include "chrome/browser/sessions/tab_restore_service_factory.h"
-#include "chrome/common/features.h"
 #include "components/browsing_data/core/features.h"
-#include "components/sessions/core/serialized_navigation_entry.h"
-#include "components/sessions/core/tab_restore_service.h"
-#include "components/sessions/core/tab_restore_service_observer.h"
 #include "content/public/browser/navigation_controller.h"
 #include "content/public/browser/navigation_entry.h"
 #include "content/public/browser/web_contents.h"
@@ -26,11 +21,6 @@
 #include "chrome/browser/ui/tabs/tab_strip_model.h"
 #endif
 
-#if BUILDFLAG(ENABLE_SESSION_SERVICE)
-#include "chrome/browser/sessions/session_service.h"
-#include "chrome/browser/sessions/session_service_factory.h"
-#endif  // BUILDFLAG(ENABLE_SESSION_SERVICE)
-
 namespace {
 
 bool TimeRangeMatcher(base::Time begin,
@@ -40,30 +30,28 @@
          (entry.GetTimestamp() < end || end.is_null());
 }
 
-bool TimeRangeMatcherForSession(
-    base::Time begin,
-    base::Time end,
-    const sessions::SerializedNavigationEntry& entry) {
-  return begin <= entry.timestamp() &&
-         (entry.timestamp() < end || end.is_null());
-}
-
 bool UrlMatcher(const base::flat_set<GURL>& urls,
                 const content::NavigationEntry& entry) {
   return urls.find(entry.GetURL()) != urls.end();
 }
 
-bool UrlMatcherForSession(const base::flat_set<GURL>& urls,
-                          const sessions::SerializedNavigationEntry& entry) {
-  return urls.find(entry.virtual_url()) != urls.end();
-}
-
-base::flat_set<GURL> CreateUrlSet(const history::URLRows& deleted_rows) {
+// Create a predicate to select NavigationEntries that should be deleted.
+// If a valid time_range is supplied, the decision will be based on time and
+// |deleted_rows| is ignored.
+// Otherwise entries matching |deleted_rows| will be selected.
+content::NavigationController::DeletionPredicate CreateDeletionPredicate(
+    const history::DeletionTimeRange& time_range,
+    const history::URLRows& deleted_rows) {
+  if (time_range.IsValid()) {
+    return base::BindRepeating(&TimeRangeMatcher, time_range.begin(),
+                               time_range.end());
+  }
   std::vector<GURL> urls;
   for (const history::URLRow& row : deleted_rows) {
     urls.push_back(row.url());
   }
-  return base::flat_set<GURL>(std::move(urls));
+  return base::BindRepeating(&UrlMatcher,
+                             base::flat_set<GURL>(std::move(urls)));
 }
 
 // Desktop is using |TabStripModel|, Android |TabModel|. They don't have a
@@ -85,14 +73,20 @@
   }
 }
 
-void DeleteTabNavigationEntries(Profile* profile,
-                                const history::DeletionTimeRange& time_range,
-                                const base::flat_set<GURL>& url_set) {
-  auto predicate =
-      time_range.IsValid()
-          ? base::BindRepeating(&TimeRangeMatcher, time_range.begin(),
-                                time_range.end())
-          : base::BindRepeating(&UrlMatcher, base::ConstRef(url_set));
+}  // namespace
+
+namespace browsing_data {
+
+void RemoveNavigationEntries(Profile* profile,
+                             const history::DeletionTimeRange& time_range,
+                             const history::URLRows& deleted_rows) {
+  DCHECK(profile->GetProfileType() == Profile::ProfileType::REGULAR_PROFILE);
+  if (!base::FeatureList::IsEnabled(
+          browsing_data::features::kRemoveNavigationHistory)) {
+    return;
+  }
+
+  auto predicate = CreateDeletionPredicate(time_range, deleted_rows);
 
 #if defined(OS_ANDROID)
   for (auto it = TabModelList::begin(); it != TabModelList::end(); ++it) {
@@ -111,95 +105,4 @@
 #endif
 }
 
-void PerformTabRestoreDeletion(
-    sessions::TabRestoreService* service,
-    const sessions::TabRestoreService::DeletionPredicate& predicate) {
-  service->DeleteNavigationEntries(predicate);
-  service->DeleteLastSession();
-}
-
-// This class waits until TabRestoreService is loaded, then deletes
-// navigation entries using |predicate| and deletes itself afterwards.
-class TabRestoreDeletionHelper : public sessions::TabRestoreServiceObserver {
- public:
-  TabRestoreDeletionHelper(
-      sessions::TabRestoreService* service,
-      const sessions::TabRestoreService::DeletionPredicate& predicate)
-      : service_(service), deletion_predicate_(predicate) {
-    DCHECK(!service->IsLoaded());
-    service->AddObserver(this);
-    service->LoadTabsFromLastSession();
-  }
-
-  // sessions::TabRestoreServiceObserver:
-  void TabRestoreServiceDestroyed(
-      sessions::TabRestoreService* service) override {
-    delete this;
-  }
-
-  void TabRestoreServiceLoaded(sessions::TabRestoreService* service) override {
-    PerformTabRestoreDeletion(service, deletion_predicate_);
-    delete this;
-  }
-
- private:
-  ~TabRestoreDeletionHelper() override { service_->RemoveObserver(this); }
-
-  sessions::TabRestoreService* service_;
-  sessions::TabRestoreService::DeletionPredicate deletion_predicate_;
-
-  DISALLOW_COPY_AND_ASSIGN(TabRestoreDeletionHelper);
-};
-
-void DeleteTabRestoreEntries(Profile* profile,
-                             const history::DeletionTimeRange& time_range,
-                             const base::flat_set<GURL>& url_set) {
-  sessions::TabRestoreService* tab_service =
-      TabRestoreServiceFactory::GetForProfile(profile);
-  if (tab_service) {
-    auto predicate =
-        time_range.IsValid()
-            ? base::BindRepeating(&TimeRangeMatcherForSession,
-                                  time_range.begin(), time_range.end())
-            : base::BindRepeating(&UrlMatcherForSession, url_set);
-    if (tab_service->IsLoaded()) {
-      PerformTabRestoreDeletion(tab_service, predicate);
-    } else {
-      // The helper deletes itself when the tab entry deletion is finished.
-      new TabRestoreDeletionHelper(tab_service, predicate);
-    }
-  }
-}
-
-void DeleteLastSessionFromSessionService(Profile* profile) {
-#if BUILDFLAG(ENABLE_SESSION_SERVICE)
-  SessionService* session_service =
-      SessionServiceFactory::GetForProfile(profile);
-  if (session_service)
-    session_service->DeleteLastSession();
-#endif
-}
-
-}  // namespace
-
-namespace browsing_data {
-
-void RemoveNavigationEntries(Profile* profile,
-                             const history::DeletionTimeRange& time_range,
-                             const history::URLRows& deleted_rows) {
-  DCHECK(profile->GetProfileType() == Profile::ProfileType::REGULAR_PROFILE);
-  if (!base::FeatureList::IsEnabled(
-          browsing_data::features::kRemoveNavigationHistory)) {
-    return;
-  }
-
-  base::flat_set<GURL> url_set;
-  if (!time_range.IsValid())
-    url_set = CreateUrlSet(deleted_rows);
-
-  DeleteTabNavigationEntries(profile, time_range, url_set);
-  DeleteTabRestoreEntries(profile, time_range, url_set);
-  DeleteLastSessionFromSessionService(profile);
-}
-
 }  // namespace browsing_data
diff --git a/chrome/browser/browsing_data/navigation_entry_remover.h b/chrome/browser/browsing_data/navigation_entry_remover.h
index 6702af4..541c762e 100644
--- a/chrome/browser/browsing_data/navigation_entry_remover.h
+++ b/chrome/browser/browsing_data/navigation_entry_remover.h
@@ -13,8 +13,6 @@
 namespace browsing_data {
 
 // Remove navigation entries from the tabs of all browsers of |profile|.
-// Recent tabs will be cleaned up as well and the session will be rewritten.
-// The last session will be removed as it can't be cleaned up easily.
 // If a valid time_range is supplied, all entries within this time range will be
 // removed and |deleted_rows| is ignored.
 // Otherwise entries matching |deleted_rows| will be deleted.
diff --git a/chrome/browser/browsing_data/navigation_entry_remover_browsertest.cc b/chrome/browser/browsing_data/navigation_entry_remover_browsertest.cc
index a3a05b2..b483883 100644
--- a/chrome/browser/browsing_data/navigation_entry_remover_browsertest.cc
+++ b/chrome/browser/browsing_data/navigation_entry_remover_browsertest.cc
@@ -6,15 +6,12 @@
 #include "base/test/scoped_feature_list.h"
 #include "build/build_config.h"
 #include "chrome/browser/browsing_data/navigation_entry_remover.h"
-#include "chrome/browser/sessions/tab_restore_service_factory.h"
 #include "chrome/browser/ui/browser.h"
-#include "chrome/browser/ui/browser_commands.h"
 #include "chrome/browser/ui/browser_list.h"
 #include "chrome/browser/ui/tabs/tab_strip_model.h"
 #include "chrome/test/base/in_process_browser_test.h"
 #include "chrome/test/base/ui_test_utils.h"
 #include "components/browsing_data/core/features.h"
-#include "components/sessions/core/tab_restore_service.h"
 #include "content/public/browser/navigation_controller.h"
 #include "content/public/browser/navigation_entry.h"
 #include "content/public/browser/notification_service.h"
@@ -241,30 +238,3 @@
 
   ExpectEntries({url_b_}, GetEntries());
 }
-
-IN_PROC_BROWSER_TEST_F(NavigationEntryRemoverTest, RecentTabDeletion) {
-  AddNavigations(browser(), {url_a_, url_b_});
-  AddTab(browser(), {url_c_});
-  AddTab(browser(), {url_d_});
-  chrome::CloseTab(browser());
-  chrome::CloseTab(browser());
-
-  sessions::TabRestoreService* tab_service =
-      TabRestoreServiceFactory::GetForProfile(browser()->profile());
-  EXPECT_EQ(2U, tab_service->entries().size());
-
-  browsing_data::RemoveNavigationEntries(
-      browser()->profile(), history::DeletionTimeRange::Invalid(),
-      {history::URLResult(url_c_, base::Time())});
-  content::RunAllTasksUntilIdle();
-  EXPECT_EQ(1U, tab_service->entries().size());
-  auto* tab = static_cast<sessions::TabRestoreService::Tab*>(
-      tab_service->entries().front().get());
-  EXPECT_EQ(url_d_, tab->navigations.back().virtual_url());
-  EXPECT_TRUE(tab_service->IsLoaded());
-
-  browsing_data::RemoveNavigationEntries(
-      browser()->profile(), history::DeletionTimeRange::Invalid(),
-      {history::URLResult(url_d_, base::Time())});
-  EXPECT_EQ(0U, tab_service->entries().size());
-}
diff --git a/chrome/browser/chrome_content_browser_client.cc b/chrome/browser/chrome_content_browser_client.cc
index 161fbc4..8eb9280 100644
--- a/chrome/browser/chrome_content_browser_client.cc
+++ b/chrome/browser/chrome_content_browser_client.cc
@@ -248,6 +248,7 @@
 #include "third_party/WebKit/public/platform/modules/webshare/webshare.mojom.h"
 #include "ui/base/l10n/l10n_util.h"
 #include "ui/base/resource/resource_bundle.h"
+#include "ui/base/ui_base_features.h"
 #include "ui/base/ui_features.h"
 #include "ui/resources/grit/ui_resources.h"
 #include "url/gurl.h"
@@ -324,7 +325,6 @@
 #include "chrome/browser/payments/payment_request_factory.h"
 #include "chrome/browser/search/instant_service.h"
 #include "chrome/browser/search/instant_service_factory.h"
-#include "chrome/browser/ui/search/new_tab_page_navigation_throttle.h"
 #include "chrome/common/importer/profile_import.mojom.h"
 #endif
 
@@ -3248,7 +3248,7 @@
       l10n_util::GetStringUTF16(IDS_UTILITY_PROCESS_UNZIP_NAME);
 
 #if defined(OS_CHROMEOS)
-  if (base::CommandLine::ForCurrentProcess()->HasSwitch(switches::kMash))
+  if (base::FeatureList::IsEnabled(features::kMash))
     mash_service_registry::RegisterOutOfProcessServices(services);
 #endif
 }
@@ -3530,11 +3530,6 @@
       DevToolsWindow::MaybeCreateNavigationThrottle(handle);
   if (devtools_throttle)
     throttles.push_back(std::move(devtools_throttle));
-
-  std::unique_ptr<content::NavigationThrottle> new_tab_page_throttle =
-      NewTabPageNavigationThrottle::MaybeCreateThrottleFor(handle);
-  if (new_tab_page_throttle)
-    throttles.push_back(std::move(new_tab_page_throttle));
 #endif
 
   return throttles;
diff --git a/chrome/browser/chromeos/arc/video/gpu_arc_video_service_host.cc b/chrome/browser/chromeos/arc/video/gpu_arc_video_service_host.cc
index 491f36e..1582347 100644
--- a/chrome/browser/chromeos/arc/video/gpu_arc_video_service_host.cc
+++ b/chrome/browser/chromeos/arc/video/gpu_arc_video_service_host.cc
@@ -22,7 +22,7 @@
 #include "services/service_manager/public/cpp/connector.h"
 #include "services/ui/public/interfaces/arc.mojom.h"
 #include "services/ui/public/interfaces/constants.mojom.h"
-#include "ui/base/ui_base_switches_util.h"
+#include "ui/base/ui_base_features.h"
 
 namespace arc {
 
@@ -49,7 +49,9 @@
 
 class VideoAcceleratorFactoryService : public mojom::VideoAcceleratorFactory {
  public:
-  VideoAcceleratorFactoryService() { DCHECK(!switches::IsMusHostingViz()); }
+  VideoAcceleratorFactoryService() {
+    DCHECK(!base::FeatureList::IsEnabled(features::kMash));
+  }
 
   ~VideoAcceleratorFactoryService() override = default;
 
@@ -79,7 +81,7 @@
     : public mojom::VideoAcceleratorFactory {
  public:
   VideoAcceleratorFactoryServiceViz() {
-    DCHECK(switches::IsMusHostingViz());
+    DCHECK(base::FeatureList::IsEnabled(features::kMash));
     DETACH_FROM_THREAD(thread_checker_);
     auto* connector =
         content::ServiceManagerConnection::GetForProcess()->GetConnector();
@@ -112,7 +114,7 @@
 
 std::unique_ptr<mojom::VideoAcceleratorFactory>
 CreateVideoAcceleratorFactory() {
-  if (switches::IsMusHostingViz())
+  if (base::FeatureList::IsEnabled(features::kMash))
     return std::make_unique<VideoAcceleratorFactoryServiceViz>();
   return std::make_unique<VideoAcceleratorFactoryService>();
 }
diff --git a/chrome/browser/chromeos/ash_config.cc b/chrome/browser/chromeos/ash_config.cc
index 01a9334..54d61e5 100644
--- a/chrome/browser/chromeos/ash_config.cc
+++ b/chrome/browser/chromeos/ash_config.cc
@@ -4,21 +4,18 @@
 
 #include "chrome/browser/chromeos/ash_config.h"
 
-#include "base/command_line.h"
-#include "chrome/common/chrome_switches.h"
 #include "services/service_manager/runner/common/client_util.h"
-#include "ui/base/ui_base_switches.h"
+#include "ui/base/ui_base_features.h"
 
 namespace chromeos {
 
 namespace {
 
 ash::Config ComputeAshConfig() {
-  base::CommandLine* command_line = base::CommandLine::ForCurrentProcess();
   ash::Config config = ash::Config::CLASSIC;
-  if (command_line->HasSwitch(switches::kMash))
+  if (base::FeatureList::IsEnabled(features::kMash))
     config = ash::Config::MASH;
-  else if (command_line->HasSwitch(switches::kMus))
+  else if (base::FeatureList::IsEnabled(features::kMus))
     config = ash::Config::MUS;
   VLOG_IF(1, config != ash::Config::CLASSIC &&
                  !service_manager::ServiceManagerIsRemote())
diff --git a/chrome/browser/chromeos/background/ash_wallpaper_delegate.cc b/chrome/browser/chromeos/background/ash_wallpaper_delegate.cc
index f7e0654..74d90a1 100644
--- a/chrome/browser/chromeos/background/ash_wallpaper_delegate.cc
+++ b/chrome/browser/chromeos/background/ash_wallpaper_delegate.cc
@@ -4,51 +4,18 @@
 
 #include "chrome/browser/chromeos/background/ash_wallpaper_delegate.h"
 
-#include "ash/shell.h"
 #include "ash/wallpaper/wallpaper_delegate.h"
-#include "ash/wm/window_animation_types.h"
-#include "ash/wm/window_animations.h"
-#include "base/command_line.h"
-#include "base/logging.h"
 #include "base/macros.h"
-#include "chrome/browser/chrome_notification_types.h"
-#include "chrome/browser/chromeos/login/wizard_controller.h"
-#include "chromeos/chromeos_switches.h"
-#include "chromeos/login/login_state.h"
-#include "components/user_manager/user.h"
-#include "components/user_manager/user_manager.h"
-#include "content/public/browser/notification_service.h"
 
 namespace chromeos {
 
 namespace {
 
-bool IsNormalWallpaperChange() {
-  if (chromeos::LoginState::Get()->IsUserLoggedIn() ||
-      !base::CommandLine::ForCurrentProcess()->HasSwitch(
-          switches::kFirstExecAfterBoot) ||
-      WizardController::IsZeroDelayEnabled() ||
-      !base::CommandLine::ForCurrentProcess()->HasSwitch(
-          switches::kLoginManager)) {
-    return true;
-  }
-
-  return false;
-}
-
 class WallpaperDelegate : public ash::WallpaperDelegate {
  public:
-  WallpaperDelegate()
-      : boot_animation_finished_(false),
-        animation_duration_override_in_ms_(0) {}
+  WallpaperDelegate() = default;
 
-  ~WallpaperDelegate() override {}
-
-  int GetAnimationType() override {
-    return ShouldShowInitialAnimation()
-               ? ash::wm::WINDOW_VISIBILITY_ANIMATION_TYPE_BRIGHTNESS_GRAYSCALE
-               : static_cast<int>(::wm::WINDOW_VISIBILITY_ANIMATION_TYPE_FADE);
-  }
+  ~WallpaperDelegate() override = default;
 
   int GetAnimationDurationOverride() override {
     return animation_duration_override_in_ms_;
@@ -58,29 +25,9 @@
     animation_duration_override_in_ms_ = animation_duration_in_ms;
   }
 
-  bool ShouldShowInitialAnimation() override {
-    if (IsNormalWallpaperChange() || boot_animation_finished_)
-      return false;
-    return true;
-  }
-
-  void OnWallpaperAnimationFinished() override {
-    content::NotificationService::current()->Notify(
-        chrome::NOTIFICATION_WALLPAPER_ANIMATION_FINISHED,
-        content::NotificationService::AllSources(),
-        content::NotificationService::NoDetails());
-  }
-
-  void OnWallpaperBootAnimationFinished() override {
-    // Make sure that boot animation type is used only once.
-    boot_animation_finished_ = true;
-  }
-
  private:
-  bool boot_animation_finished_;
-
   // The animation duration to show a new wallpaper if an animation is required.
-  int animation_duration_override_in_ms_;
+  int animation_duration_override_in_ms_ = 0;
 
   DISALLOW_COPY_AND_ASSIGN(WallpaperDelegate);
 };
diff --git a/chrome/browser/chromeos/file_system_provider/notification_manager.cc b/chrome/browser/chromeos/file_system_provider/notification_manager.cc
index f985527..4189dfcf 100644
--- a/chrome/browser/chromeos/file_system_provider/notification_manager.cc
+++ b/chrome/browser/chromeos/file_system_provider/notification_manager.cc
@@ -13,7 +13,6 @@
 #include "components/signin/core/account_id/account_id.h"
 #include "ui/base/l10n/l10n_util.h"
 #include "ui/message_center/public/cpp/notification.h"
-#include "ui/message_center/public/cpp/notification_delegate.h"
 #include "ui/message_center/public/cpp/notification_types.h"
 #include "ui/message_center/public/cpp/notifier_id.h"
 
@@ -24,29 +23,6 @@
 // Extension icon size for the notification.
 const int kIconSize = 48;
 
-// Forwards notification events to the notification manager.
-class ProviderNotificationDelegate
-    : public message_center::NotificationDelegate {
- public:
-  // Passing a raw pointer is safe here, since the life of each notification is
-  // shorter than life of the |notification_manager|.
-  explicit ProviderNotificationDelegate(
-      NotificationManager* notification_manager)
-      : notification_manager_(notification_manager) {}
-
-  void ButtonClick(int button_index) override {
-    notification_manager_->OnButtonClick(button_index);
-  }
-
-  void Close(bool by_user) override { notification_manager_->OnClose(); }
-
- private:
-  ~ProviderNotificationDelegate() override {}
-  NotificationManager* notification_manager_;  // Not owned.
-
-  DISALLOW_COPY_AND_ASSIGN(ProviderNotificationDelegate);
-};
-
 }  // namespace
 
 NotificationManager::NotificationManager(
@@ -55,7 +31,8 @@
     : profile_(profile),
       file_system_info_(file_system_info),
       icon_loader_(
-          new extensions::ChromeAppIconLoader(profile, kIconSize, this)) {
+          new extensions::ChromeAppIconLoader(profile, kIconSize, this)),
+      weak_factory_(this) {
   DCHECK_EQ(ProviderId::EXTENSION, file_system_info.provider_id().GetType());
 }
 
@@ -84,11 +61,11 @@
   }
 }
 
-void NotificationManager::OnButtonClick(int button_index) {
+void NotificationManager::ButtonClick(int button_index) {
   OnNotificationResult(ABORT);
 }
 
-void NotificationManager::OnClose() {
+void NotificationManager::Close(bool by_user) {
   OnNotificationResult(CONTINUE);
 }
 
@@ -127,7 +104,8 @@
       extension_icon_.get() ? *extension_icon_.get() : gfx::Image(),
       base::string16(),  // display_source
       GURL(), notifier_id, rich_notification_data,
-      new ProviderNotificationDelegate(this));
+      base::MakeRefCounted<message_center::ThunkNotificationDelegate>(
+          weak_factory_.GetWeakPtr()));
   notification.SetSystemPriority();
 
   NotificationDisplayService::GetForProfile(profile_)->Display(
diff --git a/chrome/browser/chromeos/file_system_provider/notification_manager.h b/chrome/browser/chromeos/file_system_provider/notification_manager.h
index f115e93..3652c38c 100644
--- a/chrome/browser/chromeos/file_system_provider/notification_manager.h
+++ b/chrome/browser/chromeos/file_system_provider/notification_manager.h
@@ -11,9 +11,11 @@
 
 #include "base/callback.h"
 #include "base/macros.h"
+#include "base/memory/weak_ptr.h"
 #include "chrome/browser/chromeos/file_system_provider/notification_manager_interface.h"
 #include "chrome/browser/chromeos/file_system_provider/provided_file_system_info.h"
 #include "chrome/browser/ui/app_icon_loader.h"
+#include "ui/message_center/public/cpp/notification_delegate.h"
 
 class Profile;
 
@@ -29,7 +31,8 @@
 // up to one notification. If more than one request is unresponsive, then
 // all of them will be aborted when clicking on the notification button.
 class NotificationManager : public NotificationManagerInterface,
-                            public AppIconLoaderDelegate {
+                            public AppIconLoaderDelegate,
+                            public message_center::NotificationObserver {
  public:
   NotificationManager(Profile* profile,
                       const ProvidedFileSystemInfo& file_system_info);
@@ -41,16 +44,14 @@
       const NotificationCallback& callback) override;
   void HideUnresponsiveNotification(int id) override;
 
-  // Invoked when a button on the notification is clicked.
-  void OnButtonClick(int button_index);
-
-  // Invoked when the notification got closed either by user or by system.
-  void OnClose();
-
   // AppIconLoaderDelegate overrides:
   void OnAppImageUpdated(const std::string& id,
                          const gfx::ImageSkia& image) override;
 
+  // message_center::NotificationObserver overrides:
+  void ButtonClick(int button_index) override;
+  void Close(bool by_user) override;
+
  private:
   typedef std::map<int, NotificationCallback> CallbackMap;
 
@@ -70,6 +71,7 @@
   CallbackMap callbacks_;
   std::unique_ptr<AppIconLoader> icon_loader_;
   std::unique_ptr<gfx::Image> extension_icon_;
+  base::WeakPtrFactory<NotificationManager> weak_factory_;
 
   DISALLOW_COPY_AND_ASSIGN(NotificationManager);
 };
diff --git a/chrome/browser/chromeos/first_run/goodies_displayer_browsertest.cc b/chrome/browser/chromeos/first_run/goodies_displayer_browsertest.cc
index b41ac4c..123a12ba 100644
--- a/chrome/browser/chromeos/first_run/goodies_displayer_browsertest.cc
+++ b/chrome/browser/chromeos/first_run/goodies_displayer_browsertest.cc
@@ -15,7 +15,6 @@
 #include "chrome/test/base/test_launcher_utils.h"
 #include "chrome/test/base/ui_test_utils.h"
 #include "components/prefs/pref_service.h"
-#include "ui/base/ui_base_switches.h"
 
 namespace chromeos {
 
@@ -86,10 +85,6 @@
   // InProcessBrowserTest overrides.
   void SetUpDefaultCommandLine(base::CommandLine* command_line) override {
     base::CommandLine default_command_line(base::CommandLine::NO_PROGRAM);
-    const char* kSwitchesToCopy[] = {switches::kMash, switches::kMus,
-                                     switches::kMusHostingViz};
-    default_command_line.CopySwitchesFrom(*command_line, kSwitchesToCopy,
-                                          arraysize(kSwitchesToCopy));
     InProcessBrowserTest::SetUpDefaultCommandLine(&default_command_line);
     if (NoFirstRunSpecified()) {  // --no-first-run is present by default.
       *command_line = default_command_line;
diff --git a/chrome/browser/chromeos/login/chrome_restart_request.cc b/chrome/browser/chromeos/login/chrome_restart_request.cc
index f8a442c..a57805db 100644
--- a/chrome/browser/chromeos/login/chrome_restart_request.cc
+++ b/chrome/browser/chromeos/login/chrome_restart_request.cc
@@ -146,7 +146,6 @@
     ::switches::kPpapiInProcess,
     ::switches::kRemoteDebuggingPort,
     ::switches::kRendererStartupDialog,
-    ::switches::kRootLayerScrolls,
     ::switches::kTouchCalibration,
     ::switches::kTouchDevices,
     ::switches::kTouchEventFeatureDetection,
diff --git a/chrome/browser/chromeos/login/lock/views_screen_locker.cc b/chrome/browser/chromeos/login/lock/views_screen_locker.cc
index 31241a63..1f313fb 100644
--- a/chrome/browser/chromeos/login/lock/views_screen_locker.cc
+++ b/chrome/browser/chromeos/login/lock/views_screen_locker.cc
@@ -5,6 +5,7 @@
 #include "chrome/browser/chromeos/login/lock/views_screen_locker.h"
 
 #include <memory>
+#include <string>
 #include <utility>
 
 #include "base/bind.h"
@@ -143,29 +144,7 @@
 }
 
 void ViewsScreenLocker::OnAshLockAnimationFinished() {
-  // Notify session controller that the lock animations are done.
-  // This is used to notify chromeos::PowerEventObserver that lock screen UI
-  // has finished showing. PowerEventObserver uses this notification during
-  // device suspend - device suspend is delayed until lock UI reports it's done
-  // animating. Additionally, PowerEventObserver will not stop root windows
-  // compositors until it receives this notification.
-  // Historically, this was called when Web UI lock implementation reported
-  // that all animations for showing the UI have finished, which gave enough
-  // time to update display's frame buffers with new UI before compositing was
-  // stopped.
-  // This is not the case with views lock implementation.
-  // OnAshLockAnimationFinished() is called too soon, thus the display's frame
-  // buffers might still contain the UI from before the lock window was shown
-  // at this time - see https://crbug.com/807511.
-  // To work around this, add additional delay before notifying
-  // PowerEventObserver lock screen UI is ready.
-  // TODO(tbarzic): Find a more deterministic way to determine when the display
-  //     can be turned off during device suspend.
-  base::ThreadTaskRunnerHandle::Get()->PostDelayedTask(
-      FROM_HERE,
-      base::BindOnce(&ViewsScreenLocker::NotifyChromeLockAnimationsComplete,
-                     weak_factory_.GetWeakPtr()),
-      base::TimeDelta::FromMilliseconds(1500));
+  SessionControllerClient::Get()->NotifyChromeLockAnimationsComplete();
 }
 
 void ViewsScreenLocker::SetFingerprintState(
@@ -306,10 +285,6 @@
   OnDevChannelInfoUpdated();
 }
 
-void ViewsScreenLocker::NotifyChromeLockAnimationsComplete() {
-  SessionControllerClient::Get()->NotifyChromeLockAnimationsComplete();
-}
-
 void ViewsScreenLocker::UpdatePinKeyboardState(const AccountId& account_id) {
   quick_unlock::QuickUnlockStorage* quick_unlock_storage =
       quick_unlock::QuickUnlockFactory::GetForAccountId(account_id);
diff --git a/chrome/browser/chromeos/login/lock/views_screen_locker.h b/chrome/browser/chromeos/login/lock/views_screen_locker.h
index c2ad69175..fbf9d52 100644
--- a/chrome/browser/chromeos/login/lock/views_screen_locker.h
+++ b/chrome/browser/chromeos/login/lock/views_screen_locker.h
@@ -84,9 +84,6 @@
   void OnAllowedInputMethodsChanged();
   void OnDevChannelInfoUpdated();
 
-  // Notifies the session manager that the lock animations are complete.
-  void NotifyChromeLockAnimationsComplete();
-
   std::unique_ptr<UserSelectionScreenProxy> user_selection_screen_proxy_;
   std::unique_ptr<UserSelectionScreen> user_selection_screen_;
 
diff --git a/chrome/browser/chromeos/login/session/user_session_manager.cc b/chrome/browser/chromeos/login/session/user_session_manager.cc
index aa77f48..1dd44b5 100644
--- a/chrome/browser/chromeos/login/session/user_session_manager.cc
+++ b/chrome/browser/chromeos/login/session/user_session_manager.cc
@@ -299,11 +299,6 @@
     return false;
   }
 
-  // TODO(mfomitchev): Browser restart doesn't currently work in Mus+ash.
-  // So if we are running Mustash and we need to restart - just crash right
-  // here. crbug.com/690140
-  CHECK(!user_flags.HasSwitch(::switches::kMash));
-
   return true;
 }
 
diff --git a/chrome/browser/chromeos/login/ui/login_display_host_webui.cc b/chrome/browser/chromeos/login/ui/login_display_host_webui.cc
index 14fdbdab..d451706 100644
--- a/chrome/browser/chromeos/login/ui/login_display_host_webui.cc
+++ b/chrome/browser/chromeos/login/ui/login_display_host_webui.cc
@@ -12,7 +12,6 @@
 #include "ash/public/cpp/shell_window_ids.h"
 #include "ash/shell.h"
 #include "ash/system/tray/system_tray.h"
-#include "ash/wallpaper/wallpaper_delegate.h"
 #include "base/bind.h"
 #include "base/command_line.h"
 #include "base/location.h"
@@ -801,13 +800,6 @@
   } else if (chrome::NOTIFICATION_WALLPAPER_ANIMATION_FINISHED == type) {
     VLOG(1) << "Login WebUI >> wp animation done";
     is_wallpaper_loaded_ = true;
-    if (!ash_util::IsRunningInMash()) {
-      ash::Shell::Get()
-          ->wallpaper_delegate()
-          ->OnWallpaperBootAnimationFinished();
-    } else {
-      NOTIMPLEMENTED();
-    }
     if (waiting_for_wallpaper_load_) {
       // StartWizard / StartSignInScreen could be called multiple times through
       // the lifetime of host.
diff --git a/chrome/browser/chromeos/policy/device_local_account_policy_store.cc b/chrome/browser/chromeos/policy/device_local_account_policy_store.cc
index 510e98f..3353ec4 100644
--- a/chrome/browser/chromeos/policy/device_local_account_policy_store.cc
+++ b/chrome/browser/chromeos/policy/device_local_account_policy_store.cc
@@ -29,7 +29,8 @@
     chromeos::SessionManagerClient* session_manager_client,
     chromeos::DeviceSettingsService* device_settings_service,
     scoped_refptr<base::SequencedTaskRunner> background_task_runner)
-    : UserCloudPolicyStoreBase(background_task_runner),
+    : UserCloudPolicyStoreBase(background_task_runner,
+                               PolicyScope::POLICY_SCOPE_USER),
       account_id_(account_id),
       session_manager_client_(session_manager_client),
       device_settings_service_(device_settings_service),
diff --git a/chrome/browser/chromeos/policy/user_cloud_policy_store_chromeos.cc b/chrome/browser/chromeos/policy/user_cloud_policy_store_chromeos.cc
index f66dcd80..38361843 100644
--- a/chrome/browser/chromeos/policy/user_cloud_policy_store_chromeos.cc
+++ b/chrome/browser/chromeos/policy/user_cloud_policy_store_chromeos.cc
@@ -40,7 +40,8 @@
     const AccountId& account_id,
     const base::FilePath& user_policy_key_dir,
     bool is_active_directory)
-    : UserCloudPolicyStoreBase(background_task_runner),
+    : UserCloudPolicyStoreBase(background_task_runner,
+                               PolicyScope::POLICY_SCOPE_USER),
       session_manager_client_(session_manager_client),
       account_id_(account_id),
       is_active_directory_(is_active_directory),
diff --git a/chrome/browser/chromeos/printing/cups_print_job_notification.cc b/chrome/browser/chromeos/printing/cups_print_job_notification.cc
index 23cd74a..ee36c928 100644
--- a/chrome/browser/chromeos/printing/cups_print_job_notification.cc
+++ b/chrome/browser/chromeos/printing/cups_print_job_notification.cc
@@ -33,30 +33,6 @@
 const char kCupsPrintJobNotificationId[] =
     "chrome://settings/printing/cups-print-job-notification";
 
-class CupsPrintJobNotificationDelegate
-    : public message_center::NotificationDelegate {
- public:
-  explicit CupsPrintJobNotificationDelegate(CupsPrintJobNotification* item)
-      : item_(item) {}
-
-  // NotificationDelegate overrides:
-  void Close(bool by_user) override {
-    if (by_user)
-      item_->CloseNotificationByUser();
-  }
-
-  void ButtonClick(int button_index) override {
-    item_->ClickOnNotificationButton(button_index);
-  }
-
- private:
-  ~CupsPrintJobNotificationDelegate() override {}
-
-  CupsPrintJobNotification* item_;
-
-  DISALLOW_COPY_AND_ASSIGN(CupsPrintJobNotificationDelegate);
-};
-
 }  // namespace
 
 CupsPrintJobNotification::CupsPrintJobNotification(
@@ -66,7 +42,8 @@
     : notification_manager_(manager),
       notification_id_(print_job->GetUniqueId()),
       print_job_(print_job),
-      profile_(profile) {
+      profile_(profile),
+      weak_factory_(this) {
   // Create a notification for the print job. The title, body, icon and buttons
   // of the notification will be updated in UpdateNotification().
   notification_ = std::make_unique<message_center::Notification>(
@@ -79,7 +56,8 @@
       message_center::NotifierId(message_center::NotifierId::SYSTEM_COMPONENT,
                                  kCupsPrintJobNotificationId),
       message_center::RichNotificationData(),
-      new CupsPrintJobNotificationDelegate(this));
+      base::MakeRefCounted<message_center::ThunkNotificationDelegate>(
+          weak_factory_.GetWeakPtr()));
   notification_->set_clickable(true);
   UpdateNotification();
 }
@@ -94,7 +72,10 @@
   UpdateNotification();
 }
 
-void CupsPrintJobNotification::CloseNotificationByUser() {
+void CupsPrintJobNotification::Close(bool by_user) {
+  if (!by_user)
+    return;
+
   closed_in_middle_ = true;
   if (!print_job_ ||
       print_job_->state() == CupsPrintJob::State::STATE_SUSPENDED) {
@@ -102,7 +83,7 @@
   }
 }
 
-void CupsPrintJobNotification::ClickOnNotificationButton(int button_index) {
+void CupsPrintJobNotification::ButtonClick(int button_index) {
   DCHECK(button_index >= 0 &&
          static_cast<size_t>(button_index) < button_commands_.size());
 
diff --git a/chrome/browser/chromeos/printing/cups_print_job_notification.h b/chrome/browser/chromeos/printing/cups_print_job_notification.h
index 38ad22a..6a815d82e 100644
--- a/chrome/browser/chromeos/printing/cups_print_job_notification.h
+++ b/chrome/browser/chromeos/printing/cups_print_job_notification.h
@@ -9,7 +9,9 @@
 #include <string>
 #include <vector>
 
+#include "base/memory/weak_ptr.h"
 #include "ui/gfx/image/image.h"
+#include "ui/message_center/public/cpp/notification_delegate.h"
 
 class Profile;
 
@@ -24,7 +26,7 @@
 
 // CupsPrintJobNotification is used to update the notification of a print job
 // according to its state and respond to the user's action.
-class CupsPrintJobNotification {
+class CupsPrintJobNotification : public message_center::NotificationObserver {
  public:
   enum class ButtonCommand {
     CANCEL_PRINTING,
@@ -36,12 +38,13 @@
   CupsPrintJobNotification(CupsPrintJobNotificationManager* manager,
                            CupsPrintJob* print_job,
                            Profile* profile);
-  ~CupsPrintJobNotification();
+  virtual ~CupsPrintJobNotification();
 
   void OnPrintJobStatusUpdated();
 
-  void CloseNotificationByUser();
-  void ClickOnNotificationButton(int button_index);
+  // message_center::NotificationObserver
+  void Close(bool by_user) override;
+  void ButtonClick(int button_index) override;
 
  private:
   // Update the notification based on the print job's status.
@@ -76,6 +79,8 @@
   // status.
   std::vector<ButtonCommand> button_commands_;
 
+  base::WeakPtrFactory<CupsPrintJobNotification> weak_factory_;
+
   DISALLOW_COPY_AND_ASSIGN(CupsPrintJobNotification);
 };
 
diff --git a/chrome/browser/chromeos/smb_client/smb_file_system.cc b/chrome/browser/chromeos/smb_client/smb_file_system.cc
index 550466940..b8000e69 100644
--- a/chrome/browser/chromeos/smb_client/smb_file_system.cc
+++ b/chrome/browser/chromeos/smb_client/smb_file_system.cc
@@ -298,7 +298,10 @@
     const base::FilePath& source_path,
     const base::FilePath& target_path,
     const storage::AsyncFileUtil::StatusCallback& callback) {
-  NOTIMPLEMENTED();
+  GetSmbProviderClient()->CopyEntry(
+      GetMountId(), source_path, target_path,
+      base::BindOnce(&SmbFileSystem::HandleStatusCallback,
+                     weak_ptr_factory_.GetWeakPtr(), callback));
   return CreateAbortCallback();
 }
 
diff --git a/chrome/browser/conflicts/module_list_filter_win.cc b/chrome/browser/conflicts/module_list_filter_win.cc
index c6ad1b7..e2b6e94 100644
--- a/chrome/browser/conflicts/module_list_filter_win.cc
+++ b/chrome/browser/conflicts/module_list_filter_win.cc
@@ -29,15 +29,23 @@
 
   // Now look at each module in the group in detail.
   for (const auto& module : module_group.modules()) {
-    // A valid entry contains both the basename and the code id.
-    if (!module.has_basename_hash() || !module.has_code_id_hash())
+    // A valid entry contains one of the basename and the code id.
+    if (!module.has_basename_hash() && !module.has_code_id_hash())
       continue;
 
-    // Match against the module.
-    if (module.basename_hash() == module_basename_hash &&
-        module.code_id_hash() == module_code_id_hash) {
-      return true;
+    // Skip this entry if it doesn't match the basename of the module.
+    if (module.has_basename_hash() &&
+        module.basename_hash() != module_basename_hash) {
+      continue;
     }
+
+    // Or the code id.
+    if (module.has_code_id_hash() &&
+        module.code_id_hash() != module_code_id_hash) {
+      continue;
+    }
+
+    return true;
   }
 
   return false;
diff --git a/chrome/browser/conflicts/module_list_filter_win_unittest.cc b/chrome/browser/conflicts/module_list_filter_win_unittest.cc
index 421aec25..7b81835 100644
--- a/chrome/browser/conflicts/module_list_filter_win_unittest.cc
+++ b/chrome/browser/conflicts/module_list_filter_win_unittest.cc
@@ -11,6 +11,7 @@
 #include "base/files/file_util.h"
 #include "base/files/scoped_temp_dir.h"
 #include "base/i18n/case_conversion.h"
+#include "base/optional.h"
 #include "base/sha1.h"
 #include "base/strings/stringprintf.h"
 #include "base/strings/utf_string_conversions.h"
@@ -31,6 +32,10 @@
   return contents.size() == static_cast<size_t>(result);
 }
 
+std::string GetCodeId(uint32_t module_time_date_stamp, uint32_t module_size) {
+  return base::StringPrintf("%08X%x", module_time_date_stamp, module_size);
+}
+
 // Helper class to build and serialize a ModuleList.
 class ModuleListBuilder {
  public:
@@ -42,19 +47,31 @@
   }
 
   // Adds a module to the whitelist.
-  void AddWhitelistedModule(const ModuleInfoKey& module_key,
-                            const ModuleInfoData& module_data) {
+  void AddWhitelistedModule(base::Optional<base::string16> basename,
+                            base::Optional<std::string> code_id) {
+    CHECK(basename.has_value() || code_id.has_value());
+
     chrome::conflicts::ModuleGroup* module_group =
         module_list_.mutable_whitelist()->add_module_groups();
 
     chrome::conflicts::Module* module = module_group->add_modules();
 
-    module->set_basename_hash(base::SHA1HashString(base::UTF16ToUTF8(
-        base::i18n::ToLower(module_data.inspection_result->basename))));
+    if (basename.has_value()) {
+      module->set_basename_hash(base::SHA1HashString(
+          base::UTF16ToUTF8(base::i18n::ToLower(basename.value()))));
+    }
 
-    std::string code_id = base::StringPrintf(
-        "%08X%x", module_key.module_time_date_stamp, module_key.module_size);
-    module->set_code_id_hash(base::SHA1HashString(code_id));
+    if (code_id.has_value())
+      module->set_code_id_hash(base::SHA1HashString(code_id.value()));
+  }
+
+  // Adds a module to the whitelist. Used when both the Code ID and the basename
+  // must be set.
+  void AddWhitelistedModule(const ModuleInfoKey& module_key,
+                            const ModuleInfoData& module_data) {
+    AddWhitelistedModule(
+        module_data.inspection_result->basename,
+        GetCodeId(module_key.module_time_date_stamp, module_key.module_size));
   }
 
   // Adds a module to the blacklist.
@@ -79,9 +96,8 @@
     module->set_basename_hash(base::SHA1HashString(base::UTF16ToUTF8(
         base::i18n::ToLower(module_data.inspection_result->basename))));
 
-    std::string code_id = base::StringPrintf(
-        "%08X%x", module_key.module_time_date_stamp, module_key.module_size);
-    module->set_code_id_hash(base::SHA1HashString(code_id));
+    module->set_code_id_hash(base::SHA1HashString(
+        GetCodeId(module_key.module_time_date_stamp, module_key.module_size)));
   }
 
   // Serializes the |module_list_| to |module_list_path_|. Returns true on
@@ -190,3 +206,54 @@
   EXPECT_FALSE(
       module_list_filter().IsBlacklisted(module_2.first, module_2.second));
 }
+
+TEST_F(ModuleListFilterTest, BasenameOnly) {
+  ModuleInfo original =
+      CreateModuleInfo(base::FilePath(L"c:\\path\\basename.dll"), 1111, 0001);
+  ModuleInfo same_basename = CreateModuleInfo(
+      base::FilePath(L"c:\\wrong_path\\basename.dll"), 2222, 0002);
+  ModuleInfo same_path = CreateModuleInfo(
+      base::FilePath(L"c:\\path\\wrong_basename.dll"), 3333, 0003);
+  ModuleInfo same_code_id = CreateModuleInfo(
+      base::FilePath(L"c:\\wrong_path\\wrong_basename.dll"), 1111, 0001);
+
+  ModuleListBuilder module_list_builder(module_list_path());
+  module_list_builder.AddWhitelistedModule(
+      original.second.inspection_result->basename, base::nullopt);
+  ASSERT_TRUE(module_list_builder.Finalize());
+
+  ASSERT_TRUE(module_list_filter().Initialize(module_list_path()));
+
+  EXPECT_TRUE(
+      module_list_filter().IsWhitelisted(original.first, original.second));
+  EXPECT_TRUE(module_list_filter().IsWhitelisted(same_basename.first,
+                                                 same_basename.second));
+  EXPECT_FALSE(
+      module_list_filter().IsWhitelisted(same_path.first, same_path.second));
+  EXPECT_FALSE(module_list_filter().IsWhitelisted(same_code_id.first,
+                                                  same_code_id.second));
+}
+
+TEST_F(ModuleListFilterTest, CodeIdOnly) {
+  ModuleInfo original =
+      CreateModuleInfo(base::FilePath(L"c:\\path\\basename.dll"), 1111, 0001);
+  ModuleInfo same_basename = CreateModuleInfo(
+      base::FilePath(L"c:\\wrong_path\\basename.dll"), 2222, 0002);
+  ModuleInfo same_code_id = CreateModuleInfo(
+      base::FilePath(L"c:\\wrong_path\\wrong_basename.dll"), 1111, 0001);
+
+  ModuleListBuilder module_list_builder(module_list_path());
+  module_list_builder.AddWhitelistedModule(
+      base::nullopt, GetCodeId(original.first.module_time_date_stamp,
+                               original.first.module_size));
+  ASSERT_TRUE(module_list_builder.Finalize());
+
+  ASSERT_TRUE(module_list_filter().Initialize(module_list_path()));
+
+  EXPECT_TRUE(
+      module_list_filter().IsWhitelisted(original.first, original.second));
+  EXPECT_FALSE(module_list_filter().IsWhitelisted(same_basename.first,
+                                                  same_basename.second));
+  EXPECT_TRUE(module_list_filter().IsWhitelisted(same_code_id.first,
+                                                 same_code_id.second));
+}
diff --git a/chrome/browser/conflicts/proto/module_list.proto b/chrome/browser/conflicts/proto/module_list.proto
index 9d95830..fa39993e0 100644
--- a/chrome/browser/conflicts/proto/module_list.proto
+++ b/chrome/browser/conflicts/proto/module_list.proto
@@ -91,11 +91,11 @@
   // Indicates whether or not this module should be allowed to load. This can be
   // used to explicitly allow modules to load that when blocked cause problems,
   // but otherwise to allow messaging to encourage users to remove them.
-  required bool allow_load = 1;
+  optional bool allow_load = 1;
 
   // The URL associated with the user message. See BlacklistMessageType for full
   // details.
-  required BlacklistMessageType message_type = 2;
+  optional BlacklistMessageType message_type = 2;
   optional string message_url = 3;
 }
 
@@ -105,10 +105,10 @@
 // Next id: 3
 message BlacklistModuleGroup {
   // The action to take when modules in this group are encountered.
-  required BlacklistAction action = 1;
+  optional BlacklistAction action = 1;
 
   // The group of modules itself.
-  required ModuleGroup modules = 2;
+  optional ModuleGroup modules = 2;
 }
 
 // Describes a blacklist of modules that are to be handled specially when
@@ -122,9 +122,9 @@
 // Next id: 3
 message ModuleList {
   // The whitelisted modules.
-  required ModuleWhitelist whitelist = 1;
+  optional ModuleWhitelist whitelist = 1;
 
   // The blacklisted modules, and the associated actions to take upon
   // encountering them.
-  required ModuleBlacklist blacklist = 2;
+  optional ModuleBlacklist blacklist = 2;
 }
diff --git a/chrome/browser/download/notification/download_item_notification.cc b/chrome/browser/download/notification/download_item_notification.cc
index 359c2ac..281be8b 100644
--- a/chrome/browser/download/notification/download_item_notification.cc
+++ b/chrome/browser/download/notification/download_item_notification.cc
@@ -171,30 +171,6 @@
   }
 }
 
-// A thunk delegate class.
-class DownloadNotificationDelegate
-    : public message_center::NotificationDelegate {
- public:
-  explicit DownloadNotificationDelegate(DownloadItemNotification* notification)
-      : notification_(notification) {}
-
-  void Close(bool by_user) override { notification_->OnNotificationClose(); }
-
-  void Click() override { notification_->OnNotificationClick(); }
-
-  void ButtonClick(int index) override {
-    notification_->OnNotificationButtonClick(index);
-  }
-
- private:
-  ~DownloadNotificationDelegate() override = default;
-
-  // |notification_| is assumed to outlive |this|.
-  DownloadItemNotification* notification_;
-
-  DISALLOW_COPY_AND_ASSIGN(DownloadNotificationDelegate);
-};
-
 }  // namespace
 
 DownloadItemNotification::DownloadItemNotification(download::DownloadItem* item)
@@ -204,6 +180,7 @@
   message_center::RichNotificationData rich_notification_data;
   rich_notification_data.should_make_spoken_feedback_for_popup_updates = false;
   rich_notification_data.vector_small_image = &ash::kNotificationDownloadIcon;
+
   notification_ = std::make_unique<message_center::Notification>(
       message_center::NOTIFICATION_TYPE_PROGRESS, GetNotificationId(),
       base::string16(),  // title
@@ -215,7 +192,8 @@
       message_center::NotifierId(message_center::NotifierId::SYSTEM_COMPONENT,
                                  kDownloadNotificationNotifierId),
       rich_notification_data,
-      base::MakeRefCounted<DownloadNotificationDelegate>(this));
+      base::MakeRefCounted<message_center::ThunkNotificationDelegate>(
+          weak_factory_.GetWeakPtr()));
 
   notification_->set_progress(0);
   // Dangerous notifications don't have a click handler.
@@ -239,9 +217,11 @@
   // The given |item| may be already free'd.
   DCHECK_EQ(item, item_);
 
-  // Removing the notification causes calling |NotificationDelegate::Close()|.
   NotificationDisplayServiceFactory::GetForProfile(profile())->Close(
       NotificationHandler::Type::TRANSIENT, GetNotificationId());
+  // |this| will be deleted before there's a chance for Close() to be called
+  // through the delegate, so preemptively call it now.
+  Close(false);
 
   item_ = nullptr;
 }
@@ -259,13 +239,13 @@
       NotificationHandler::Type::TRANSIENT, *notification_);
 }
 
-void DownloadItemNotification::OnNotificationClose() {
+void DownloadItemNotification::Close(bool by_user) {
   closed_ = true;
 
   if (item_ && item_->IsDangerous() && !item_->IsDone()) {
     base::RecordAction(
         UserMetricsAction("DownloadNotification.Close_Dangerous"));
-    item_->Cancel(true /* by_user */);
+    item_->Cancel(by_user);
     return;
   }
 
@@ -275,7 +255,7 @@
   }
 }
 
-void DownloadItemNotification::OnNotificationClick() {
+void DownloadItemNotification::Click() {
   if (item_->IsDangerous()) {
     base::RecordAction(
         UserMetricsAction("DownloadNotification.Click_Dangerous"));
@@ -310,7 +290,7 @@
   }
 }
 
-void DownloadItemNotification::OnNotificationButtonClick(int button_index) {
+void DownloadItemNotification::ButtonClick(int button_index) {
   if (button_index < 0 ||
       static_cast<size_t>(button_index) >= button_actions_->size()) {
     // Out of boundary.
diff --git a/chrome/browser/download/notification/download_item_notification.h b/chrome/browser/download/notification/download_item_notification.h
index 096c659..cfeb50b 100644
--- a/chrome/browser/download/notification/download_item_notification.h
+++ b/chrome/browser/download/notification/download_item_notification.h
@@ -24,7 +24,8 @@
 class Notification;
 }
 
-class DownloadItemNotification : public ImageDecoder::ImageRequest {
+class DownloadItemNotification : public ImageDecoder::ImageRequest,
+                                 public message_center::NotificationObserver {
  public:
   explicit DownloadItemNotification(download::DownloadItem* item);
   ~DownloadItemNotification() override;
@@ -35,10 +36,10 @@
   // Disables popup by setting low priority.
   void DisablePopup();
 
-  // Called back from the NotificationDelegate.
-  void OnNotificationClose();
-  void OnNotificationClick();
-  void OnNotificationButtonClick(int button_index);
+  // NotificationObserver:
+  void Close(bool by_user) override;
+  void Click() override;
+  void ButtonClick(int button_index) override;
 
  private:
   friend class test::DownloadItemNotificationTest;
diff --git a/chrome/browser/download/notification/download_item_notification_unittest.cc b/chrome/browser/download/notification/download_item_notification_unittest.cc
index d92cd178..fa4eeb8 100644
--- a/chrome/browser/download/notification/download_item_notification_unittest.cc
+++ b/chrome/browser/download/notification/download_item_notification_unittest.cc
@@ -117,14 +117,6 @@
         download_item_notification_->GetNotificationId(), false);
   }
 
-  // Trampoline methods to access a private method in DownloadItemNotification.
-  void NotificationItemClick() {
-    return download_item_notification_->OnNotificationClick();
-  }
-  void NotificationItemButtonClick(int index) {
-    return download_item_notification_->OnNotificationButtonClick(index);
-  }
-
   bool ShownAsPopUp() { return !LookUpNotification()->shown_as_popup(); }
 
   void CreateDownloadItemNotification() {
@@ -182,13 +174,13 @@
   // Pauses and makes sure the DownloadItem::Pause() is called.
   EXPECT_CALL(*download_item_, Pause()).Times(1);
   EXPECT_CALL(*download_item_, IsPaused()).WillRepeatedly(Return(true));
-  NotificationItemButtonClick(0);
+  download_item_notification_->ButtonClick(0);
   download_item_->NotifyObserversDownloadUpdated();
 
   // Resumes and makes sure the DownloadItem::Resume() is called.
   EXPECT_CALL(*download_item_, Resume()).Times(1);
   EXPECT_CALL(*download_item_, IsPaused()).WillRepeatedly(Return(false));
-  NotificationItemButtonClick(0);
+  download_item_notification_->ButtonClick(0);
   download_item_->NotifyObserversDownloadUpdated();
 }
 
@@ -205,7 +197,7 @@
   // Clicks and confirms that the OpenDownload() is called.
   EXPECT_CALL(*download_item_, OpenDownload()).Times(1);
   EXPECT_CALL(*download_item_, SetOpenWhenComplete(_)).Times(0);
-  NotificationItemClick();
+  download_item_notification_->Click();
 }
 
 TEST_F(DownloadItemNotificationTest, OpenWhenComplete) {
@@ -219,7 +211,7 @@
   EXPECT_CALL(*download_item_, SetOpenWhenComplete(true))
       .Times(1)
       .WillOnce(Return());
-  NotificationItemClick();
+  download_item_notification_->Click();
   EXPECT_CALL(*download_item_, GetOpenWhenComplete())
       .WillRepeatedly(Return(true));
 
@@ -227,7 +219,7 @@
   EXPECT_CALL(*download_item_, SetOpenWhenComplete(false))
       .Times(1)
       .WillOnce(Return());
-  NotificationItemClick();
+  download_item_notification_->Click();
   EXPECT_CALL(*download_item_, GetOpenWhenComplete())
       .WillRepeatedly(Return(false));
 
@@ -235,7 +227,7 @@
   EXPECT_CALL(*download_item_, SetOpenWhenComplete(true))
       .Times(1)
       .WillOnce(Return());
-  NotificationItemClick();
+  download_item_notification_->Click();
   EXPECT_CALL(*download_item_, GetOpenWhenComplete())
       .WillRepeatedly(Return(true));
 
diff --git a/chrome/browser/extensions/all_urls_apitest.cc b/chrome/browser/extensions/all_urls_apitest.cc
index dfb9a57..9eb283099 100644
--- a/chrome/browser/extensions/all_urls_apitest.cc
+++ b/chrome/browser/extensions/all_urls_apitest.cc
@@ -9,8 +9,6 @@
 #include "chrome/browser/extensions/extension_service.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/ui/browser.h"
-#include "chrome/browser/ui/search/local_ntp_test_utils.h"
-#include "chrome/common/url_constants.h"
 #include "chrome/test/base/ui_test_utils.h"
 #include "components/crx_file/id_util.h"
 #include "extensions/browser/extension_registry.h"
@@ -47,15 +45,8 @@
   }
 
   void NavigateAndWait(const std::string& url) {
-    std::string expected_url = url;
-    if (url == chrome::kChromeUINewTabURL) {
-      expected_url =
-          local_ntp_test_utils::GetFinalNtpUrl(browser()->profile()).spec();
-    }
-    ExtensionTestMessageListener listener_a("content script: " + expected_url,
-                                            false);
-    ExtensionTestMessageListener listener_b("execute: " + expected_url, false);
-
+    ExtensionTestMessageListener listener_a("content script: " + url, false);
+    ExtensionTestMessageListener listener_b("execute: " + url, false);
     ui_test_utils::NavigateToURL(browser(), GURL(url));
     ASSERT_TRUE(listener_a.WaitUntilSatisfied());
     ASSERT_TRUE(listener_b.WaitUntilSatisfied());
diff --git a/chrome/browser/extensions/api/autotest_private/autotest_private_api.cc b/chrome/browser/extensions/api/autotest_private/autotest_private_api.cc
index 033ce0f..8bdc5a72 100644
--- a/chrome/browser/extensions/api/autotest_private/autotest_private_api.cc
+++ b/chrome/browser/extensions/api/autotest_private/autotest_private_api.cc
@@ -397,7 +397,7 @@
   std::unique_ptr<base::ListValue> values(new base::ListValue);
 #if defined(OS_CHROMEOS)
   // TODO(estade): we can't rely on the message center being available in the
-  // browser process (in --mash). Make autotests that use it fail loudly. See
+  // browser process (in mash). Make autotests that use it fail loudly. See
   // crbug.com/804570
   CHECK(message_center::MessageCenter::Get());
   for (auto* notification :
diff --git a/chrome/browser/extensions/api/dial/dial_apitest.cc b/chrome/browser/extensions/api/dial/dial_apitest.cc
index 67049da..1a8413a 100644
--- a/chrome/browser/extensions/api/dial/dial_apitest.cc
+++ b/chrome/browser/extensions/api/dial/dial_apitest.cc
@@ -9,6 +9,8 @@
 #include "chrome/browser/extensions/extension_apitest.h"
 #include "chrome/browser/extensions/extension_service.h"
 #include "chrome/browser/media/router/discovery/dial/dial_registry.h"
+#include "chrome/browser/media/router/providers/cast/dual_media_sink_service.h"
+#include "chrome/browser/media/router/test/noop_dual_media_sink_service.h"
 #include "extensions/common/switches.h"
 #include "extensions/test/extension_test_message_listener.h"
 #include "extensions/test/result_catcher.h"
@@ -33,6 +35,13 @@
         extensions::switches::kWhitelistedExtensionID,
         "ddchlicdkolnonkihahngkmmmjnjlkkf");
   }
+
+  void SetUp() override {
+    // Stub out DualMediaSinkService so it does not interfere with the test.
+    media_router::DualMediaSinkService::SetInstanceForTest(
+        new media_router::NoopDualMediaSinkService());
+    ExtensionApiTest::SetUp();
+  }
 };
 
 }  // namespace
diff --git a/chrome/browser/extensions/content_script_apitest.cc b/chrome/browser/extensions/content_script_apitest.cc
index 8d97ebb..5bab7e6 100644
--- a/chrome/browser/extensions/content_script_apitest.cc
+++ b/chrome/browser/extensions/content_script_apitest.cc
@@ -681,8 +681,10 @@
   ResultCatcher catcher;
   test_listener.Reply(std::string());
   ASSERT_TRUE(catcher.GetNextResult()) << catcher.message();
-  EXPECT_TRUE(search::IsInstantNTP(
-      browser()->tab_strip_model()->GetActiveWebContents()));
+  EXPECT_EQ(GURL("chrome://newtab"), browser()
+                                         ->tab_strip_model()
+                                         ->GetActiveWebContents()
+                                         ->GetLastCommittedURL());
   EXPECT_FALSE(
       did_script_inject(browser()->tab_strip_model()->GetActiveWebContents()));
 
diff --git a/chrome/browser/extensions/extension_url_rewrite_browsertest.cc b/chrome/browser/extensions/extension_url_rewrite_browsertest.cc
index c9ee6ba..fa788bd 100644
--- a/chrome/browser/extensions/extension_url_rewrite_browsertest.cc
+++ b/chrome/browser/extensions/extension_url_rewrite_browsertest.cc
@@ -10,7 +10,6 @@
 #include "chrome/browser/ui/browser.h"
 #include "chrome/browser/ui/browser_window.h"
 #include "chrome/browser/ui/location_bar/location_bar.h"
-#include "chrome/browser/ui/search/local_ntp_test_utils.h"
 #include "chrome/browser/ui/tabs/tab_strip_model.h"
 #include "chrome/browser/ui/webui/md_bookmarks/md_bookmarks_ui.h"
 #include "chrome/common/url_constants.h"
@@ -73,12 +72,7 @@
   void TestURLNotShown(const GURL& url) {
     ui_test_utils::NavigateToURL(browser(), url);
     EXPECT_EQ("", GetLocationBarText());
-    if (url == chrome::kChromeUINewTabURL) {
-      EXPECT_EQ(local_ntp_test_utils::GetFinalNtpUrl(browser()->profile()),
-                GetNavigationEntry()->GetVirtualURL());
-    } else {
-      EXPECT_EQ(url, GetNavigationEntry()->GetVirtualURL());
-    }
+    EXPECT_EQ(url, GetNavigationEntry()->GetVirtualURL());
   }
 };
 
diff --git a/chrome/browser/file_select_helper.cc b/chrome/browser/file_select_helper.cc
index 794ce0c9..f9845cf 100644
--- a/chrome/browser/file_select_helper.cc
+++ b/chrome/browser/file_select_helper.cc
@@ -23,6 +23,7 @@
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/profiles/profile_manager.h"
 #include "chrome/browser/ui/browser.h"
+#include "chrome/browser/ui/browser_dialogs.h"
 #include "chrome/browser/ui/browser_list.h"
 #include "chrome/browser/ui/chrome_select_file_policy.h"
 #include "chrome/grit/generated_resources.h"
@@ -125,11 +126,13 @@
 }  // namespace
 
 struct FileSelectHelper::ActiveDirectoryEnumeration {
-  ActiveDirectoryEnumeration() : rvh_(NULL) {}
+  explicit ActiveDirectoryEnumeration(const base::FilePath& path)
+      : rvh_(NULL), path_(path) {}
 
   std::unique_ptr<DirectoryListerDispatchDelegate> delegate_;
   std::unique_ptr<net::DirectoryLister> lister_;
   RenderViewHost* rvh_;
+  const base::FilePath path_;
   std::vector<base::FilePath> results_;
 };
 
@@ -244,8 +247,7 @@
 void FileSelectHelper::StartNewEnumeration(const base::FilePath& path,
                                            int request_id,
                                            RenderViewHost* render_view_host) {
-  std::unique_ptr<ActiveDirectoryEnumeration> entry(
-      new ActiveDirectoryEnumeration);
+  auto entry = std::make_unique<ActiveDirectoryEnumeration>(path);
   entry->rvh_ = render_view_host;
   entry->delegate_.reset(new DirectoryListerDispatchDelegate(this, request_id));
   entry->lister_.reset(new net::DirectoryLister(
@@ -266,6 +268,15 @@
   entry->results_.push_back(data.path);
 }
 
+void FileSelectHelper::LaunchConfirmationDialog(
+    const base::FilePath& path,
+    std::vector<ui::SelectedFileInfo> selected_files) {
+  ShowFolderUploadConfirmationDialog(
+      path,
+      base::BindOnce(&FileSelectHelper::NotifyRenderFrameHostAndEnd, this),
+      std::move(selected_files), web_contents_);
+}
+
 void FileSelectHelper::OnListDone(int id, int error) {
   // This entry needs to be cleaned up when this function is done.
   std::unique_ptr<ActiveDirectoryEnumeration> entry(
@@ -282,7 +293,7 @@
       FilePathListToSelectedFileInfoList(entry->results_);
 
   if (id == kFileSelectEnumerationId) {
-    NotifyRenderFrameHostAndEnd(selected_files);
+    LaunchConfirmationDialog(entry->path_, std::move(selected_files));
   } else {
     entry->rvh_->DirectoryEnumerationFinished(id, entry->results_);
     EnumerateDirectoryEnd();
diff --git a/chrome/browser/file_select_helper.h b/chrome/browser/file_select_helper.h
index d4672f1f..c967582 100644
--- a/chrome/browser/file_select_helper.h
+++ b/chrome/browser/file_select_helper.h
@@ -153,6 +153,10 @@
       const net::DirectoryLister::DirectoryListerData& data);
   virtual void OnListDone(int id, int error);
 
+  void LaunchConfirmationDialog(
+      const base::FilePath& path,
+      std::vector<ui::SelectedFileInfo> selected_files);
+
   // Cleans up and releases this instance. This must be called after the last
   // callback is received from the enumeration code.
   void EnumerateDirectoryEnd();
diff --git a/chrome/browser/flag_descriptions.cc b/chrome/browser/flag_descriptions.cc
index 07609fb..631cfe6 100644
--- a/chrome/browser/flag_descriptions.cc
+++ b/chrome/browser/flag_descriptions.cc
@@ -1238,7 +1238,7 @@
     "from the user selection based on some simple fallback logic.";
 
 const char kRemoveNavigationHistoryName[] =
-    "Remove navigation entry on history deletion";
+    "Remove navigation entries on history deletion";
 const char kRemoveNavigationHistoryDescription[] =
     "Remove a navigation entry when the corresponding history entry has been "
     "deleted.";
@@ -1416,10 +1416,6 @@
 const char kSiteSettingsDescription[] =
     "Adds new ways of viewing Site settings.";
 
-const char kSlimmingPaintInvalidationName[] = "Slimming paint invalidation.";
-const char kSlimmingPaintInvalidationDescription[] =
-    "Whether to enable a new paint invalidation system.";
-
 const char kSmoothScrollingName[] = "Smooth Scrolling";
 const char kSmoothScrollingDescription[] =
     "Animate smoothly when scrolling page content.";
@@ -2625,6 +2621,17 @@
 const char kEnableImeMenuDescription[] =
     "Enable access to the new IME menu in the Language Settings page.";
 
+const char kEnableUnifiedMultiDeviceSettingsName[] =
+    "Enable unified MultiDevice settings";
+const char kEnableUnifiedMultiDeviceSettingsDescription[] =
+    "If enabled, the Chrome OS Settings UI will include a menu for the unified "
+    "MultiDevice settings.";
+
+const char kEnableUnifiedMultiDeviceSetupName[] =
+    "Enable unified MultiDevice setup";
+const char kEnableUnifiedMultiDeviceSetupDescription[] =
+    "Enable the device to setup all MultiDevice services in a single workflow.";
+
 const char kEnableZipArchiverPackerName[] = "ZIP archiver - Packer";
 const char kEnableZipArchiverPackerDescription[] =
     "Enable the ability to archive files on Drive in the Files app";
diff --git a/chrome/browser/flag_descriptions.h b/chrome/browser/flag_descriptions.h
index 2458fa1..dda4b71b 100644
--- a/chrome/browser/flag_descriptions.h
+++ b/chrome/browser/flag_descriptions.h
@@ -866,9 +866,6 @@
 extern const char kSiteSettings[];
 extern const char kSiteSettingsDescription[];
 
-extern const char kSlimmingPaintInvalidationName[];
-extern const char kSlimmingPaintInvalidationDescription[];
-
 extern const char kSmoothScrollingName[];
 extern const char kSmoothScrollingDescription[];
 
@@ -1622,6 +1619,12 @@
 extern const char kEnableImeMenuName[];
 extern const char kEnableImeMenuDescription[];
 
+extern const char kEnableUnifiedMultiDeviceSettingsName[];
+extern const char kEnableUnifiedMultiDeviceSettingsDescription[];
+
+extern const char kEnableUnifiedMultiDeviceSetupName[];
+extern const char kEnableUnifiedMultiDeviceSetupDescription[];
+
 extern const char kEnableZipArchiverPackerName[];
 extern const char kEnableZipArchiverPackerDescription[];
 
diff --git a/chrome/browser/fullscreen_ozone.cc b/chrome/browser/fullscreen_ozone.cc
index 30e5a14..b3137254 100644
--- a/chrome/browser/fullscreen_ozone.cc
+++ b/chrome/browser/fullscreen_ozone.cc
@@ -13,6 +13,6 @@
     return false;
   }
 
-  NOTREACHED() << "For Ozone builds, only --mash launch is supported for now.";
+  NOTREACHED() << "For Ozone builds, only mash launch is supported for now.";
   return false;
 }
diff --git a/chrome/browser/io_thread.cc b/chrome/browser/io_thread.cc
index fd02fe3..9ff9ca9 100644
--- a/chrome/browser/io_thread.cc
+++ b/chrome/browser/io_thread.cc
@@ -85,7 +85,6 @@
 #include "net/http/http_server_properties_impl.h"
 #include "net/http/http_transaction_factory.h"
 #include "net/net_features.h"
-#include "net/nqe/external_estimate_provider.h"
 #include "net/nqe/network_quality_estimator_params.h"
 //#include "net/proxy/proxy_script_fetcher_impl.h"
 #include "net/proxy_resolution/pac_file_fetcher_impl.h"
@@ -115,7 +114,6 @@
 #if defined(OS_ANDROID)
 #include "base/android/build_info.h"
 #include "chrome/browser/android/data_usage/external_data_use_observer.h"
-#include "chrome/browser/android/net/external_estimate_provider_android.h"
 #include "components/data_usage/android/traffic_stats_amortizer.h"
 #include "net/cert/cert_verify_proc_android.h"
 #endif  // defined(OS_ANDROID)
@@ -538,15 +536,9 @@
     }
   }
 
-  std::unique_ptr<net::ExternalEstimateProvider> external_estimate_provider;
-#if defined(OS_ANDROID)
-  external_estimate_provider =
-      std::make_unique<chrome::android::ExternalEstimateProviderAndroid>();
-#endif  // defined(OS_ANDROID)
   // Pass ownership.
   globals_->network_quality_estimator =
       std::make_unique<net::NetworkQualityEstimator>(
-          std::move(external_estimate_provider),
           base::MakeUnique<net::NetworkQualityEstimatorParams>(
               network_quality_estimator_params),
           net_log_);
diff --git a/chrome/browser/mash_service_registry_browsertest.cc b/chrome/browser/mash_service_registry_browsertest.cc
index 822fe73..895986f 100644
--- a/chrome/browser/mash_service_registry_browsertest.cc
+++ b/chrome/browser/mash_service_registry_browsertest.cc
@@ -23,7 +23,7 @@
 using MashServiceRegistryTest = InProcessBrowserTest;
 
 IN_PROC_BROWSER_TEST_F(MashServiceRegistryTest, AshAndUiInSameProcess) {
-  // Test only applies to --mash (out-of-process ash).
+  // Test only applies to mash (out-of-process ash).
   if (chromeos::GetAshConfig() != ash::Config::MASH)
     return;
 
diff --git a/chrome/browser/media/encrypted_media_browsertest.cc b/chrome/browser/media/encrypted_media_browsertest.cc
index f912126..f5bd68b 100644
--- a/chrome/browser/media/encrypted_media_browsertest.cc
+++ b/chrome/browser/media/encrypted_media_browsertest.cc
@@ -317,24 +317,27 @@
         if (support_experimental_cdm_interface) {
           scoped_feature_list_.InitWithFeatures(
               {media::kExternalClearKeyForTesting,
-               media::kSupportExperimentalCdmInterface, media::kMojoCdm},
+               media::kSupportExperimentalCdmInterface},
               {});
         } else {
           scoped_feature_list_.InitWithFeatures(
-              {media::kExternalClearKeyForTesting, media::kMojoCdm}, {});
+              {media::kExternalClearKeyForTesting}, {});
         }
       } else {
         // Pepper CDM does not support any experimental CDM interface.
         scoped_feature_list_.InitWithFeatures(
-            {media::kExternalClearKeyForTesting}, {});
+            {media::kExternalClearKeyForTesting}, {media::kMojoCdm});
       }
     } else {
       // Experimental CDM interface is only supported with External Clear Key.
-      if (cdm_host_type == CdmHostType::kMojo) {
-        scoped_feature_list_.InitWithFeatures({media::kMojoCdm}, {});
+      if (cdm_host_type != CdmHostType::kMojo) {
+        scoped_feature_list_.InitWithFeatures({}, {media::kMojoCdm});
       }
     }
 #endif  // BUILDFLAG(ENABLE_LIBRARY_CDMS)
+
+    // Make sure we actually use MojoCdm iff host type is kMojo.
+    DCHECK_EQ(cdm_host_type == CdmHostType::kMojo, IsUsingMojoCdm());
   }
 
   // Check whether the test is actually using mojo CDM.
diff --git a/chrome/browser/media/router/BUILD.gn b/chrome/browser/media/router/BUILD.gn
index e0054252..e1d59c72 100644
--- a/chrome/browser/media/router/BUILD.gn
+++ b/chrome/browser/media/router/BUILD.gn
@@ -131,6 +131,8 @@
       "test/mock_dns_sd_registry.h",
       "test/mock_mojo_media_router.cc",
       "test/mock_mojo_media_router.h",
+      "test/noop_dual_media_sink_service.cc",
+      "test/noop_dual_media_sink_service.h",
     ]
   }
 }
diff --git a/chrome/browser/media/router/media_router_factory_unittest.cc b/chrome/browser/media/router/media_router_factory_unittest.cc
index d4fff27..6618745 100644
--- a/chrome/browser/media/router/media_router_factory_unittest.cc
+++ b/chrome/browser/media/router/media_router_factory_unittest.cc
@@ -14,20 +14,16 @@
 
 namespace media_router {
 
-namespace {
-
-std::unique_ptr<KeyedService> CreateMockMediaRouter(
-    content::BrowserContext* context) {
-  return base::WrapUnique(new MockMediaRouter);
-}
-
-}  // namespace
-
 class MediaRouterFactoryTest : public testing::Test {
  protected:
   MediaRouterFactoryTest() {}
   ~MediaRouterFactoryTest() override {}
 
+  void SetUp() override {
+    MediaRouterFactory::GetInstance()->SetTestingFactory(
+        profile(), &MockMediaRouter::Create);
+  }
+
   Profile* profile() { return &profile_; }
 
  private:
@@ -53,9 +49,6 @@
 }
 
 TEST_F(MediaRouterFactoryTest, IncognitoBrowserContextShutdown) {
-  MediaRouterFactory::GetInstance()->SetTestingFactory(profile(),
-                                                       &CreateMockMediaRouter);
-
   // Creates an incognito profile.
   profile()->GetOffTheRecordProfile();
   MockMediaRouter* router = static_cast<MockMediaRouter*>(
diff --git a/chrome/browser/media/router/mojo/media_router_desktop.cc b/chrome/browser/media/router/mojo/media_router_desktop.cc
index 806ee4c..6ab67097 100644
--- a/chrome/browser/media/router/mojo/media_router_desktop.cc
+++ b/chrome/browser/media/router/mojo/media_router_desktop.cc
@@ -42,8 +42,7 @@
   // media source.
   UpdateMediaSinks(MediaSourceForDesktop().id());
 
-  if (media_sink_service_)
-    media_sink_service_->OnUserGesture();
+  media_sink_service_->OnUserGesture();
 
 #if defined(OS_WIN)
   EnsureMdnsDiscoveryEnabled();
@@ -65,7 +64,7 @@
 
 MediaRouterDesktop::MediaRouterDesktop(content::BrowserContext* context)
     : MediaRouterMojoImpl(context),
-      media_sink_service_(nullptr),
+      media_sink_service_(DualMediaSinkService::GetInstance()),
       weak_factory_(this) {
   InitializeMediaRouteProviders();
 #if defined(OS_WIN)
@@ -150,9 +149,6 @@
 void MediaRouterDesktop::ProvideSinksToExtension() {
   DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
   DVLOG(1) << "ProvideSinksToExtension";
-  if (!media_sink_service_)
-    media_sink_service_ = DualMediaSinkService::GetInstance();
-
   // If calling |ProvideSinksToExtension| for the first time, add a callback to
   // be notified of sink updates.
   if (!media_sink_service_subscription_) {
@@ -208,8 +204,7 @@
 #if defined(OS_WIN)
 void MediaRouterDesktop::EnsureMdnsDiscoveryEnabled() {
   if (media_router::CastDiscoveryEnabled()) {
-    if (media_sink_service_)
-      media_sink_service_->StartMdnsDiscovery();
+    media_sink_service_->StartMdnsDiscovery();
   } else {
     media_route_providers_[MediaRouteProviderId::EXTENSION]
         ->EnableMdnsDiscovery();
diff --git a/chrome/browser/media/router/presentation/presentation_service_delegate_impl.cc b/chrome/browser/media/router/presentation/presentation_service_delegate_impl.cc
index 4aaadf2..18a177fa 100644
--- a/chrome/browser/media/router/presentation/presentation_service_delegate_impl.cc
+++ b/chrome/browser/media/router/presentation/presentation_service_delegate_impl.cc
@@ -653,11 +653,6 @@
   return weak_factory_.GetWeakPtr();
 }
 
-void PresentationServiceDelegateImpl::SetMediaRouterForTest(
-    MediaRouter* router) {
-  router_ = router;
-}
-
 bool PresentationServiceDelegateImpl::HasScreenAvailabilityListenerForTest(
     int render_process_id,
     int render_frame_id,
diff --git a/chrome/browser/media/router/presentation/presentation_service_delegate_impl.h b/chrome/browser/media/router/presentation/presentation_service_delegate_impl.h
index 66bd93e4..dfaead3 100644
--- a/chrome/browser/media/router/presentation/presentation_service_delegate_impl.h
+++ b/chrome/browser/media/router/presentation/presentation_service_delegate_impl.h
@@ -151,7 +151,6 @@
 
   base::WeakPtr<PresentationServiceDelegateImpl> GetWeakPtr();
 
-  void SetMediaRouterForTest(MediaRouter* router);
   bool HasScreenAvailabilityListenerForTest(
       int render_process_id,
       int render_frame_id,
diff --git a/chrome/browser/media/router/presentation/presentation_service_delegate_impl_unittest.cc b/chrome/browser/media/router/presentation/presentation_service_delegate_impl_unittest.cc
index 995a268..e3fe7d5 100644
--- a/chrome/browser/media/router/presentation/presentation_service_delegate_impl_unittest.cc
+++ b/chrome/browser/media/router/presentation/presentation_service_delegate_impl_unittest.cc
@@ -6,6 +6,7 @@
 
 #include "base/test/mock_callback.h"
 #include "build/build_config.h"
+#include "chrome/browser/media/router/media_router_factory.h"
 #include "chrome/browser/media/router/presentation/local_presentation_manager.h"
 #include "chrome/browser/media/router/presentation/local_presentation_manager_factory.h"
 #include "chrome/browser/media/router/test/mock_media_router.h"
@@ -117,7 +118,8 @@
     : public ChromeRenderViewHostTestHarness {
  public:
   PresentationServiceDelegateImplTest()
-      : delegate_impl_(nullptr),
+      : router_(nullptr),
+        delegate_impl_(nullptr),
         presentation_url1_(kPresentationUrl1),
         presentation_url2_(kPresentationUrl2),
         presentation_urls_({presentation_url1_}),
@@ -131,10 +133,12 @@
   void SetUp() override {
     ChromeRenderViewHostTestHarness::SetUp();
     content::WebContents* wc = GetWebContents();
+    router_ = static_cast<MockMediaRouter*>(
+        MediaRouterFactory::GetInstance()->SetTestingFactoryAndUse(
+            web_contents()->GetBrowserContext(), &MockMediaRouter::Create));
     ASSERT_TRUE(wc);
     PresentationServiceDelegateImpl::CreateForWebContents(wc);
     delegate_impl_ = PresentationServiceDelegateImpl::FromWebContents(wc);
-    delegate_impl_->SetMediaRouterForTest(&router_);
     SetMainFrame();
     presentation_request_ = std::make_unique<content::PresentationRequest>(
         RenderFrameHostId(main_frame_process_id_, main_frame_routing_id_),
@@ -203,8 +207,8 @@
             profile()));
   }
 
+  MockMediaRouter* router_;
   PresentationServiceDelegateImpl* delegate_impl_;
-  MockMediaRouter router_;
   const GURL presentation_url1_;
   const GURL presentation_url2_;
   std::vector<GURL> presentation_urls_;
@@ -263,7 +267,7 @@
   // result, the observer added with have an empty GURL as origin.
   int render_frame_id2 = 2;
 
-  EXPECT_CALL(router_, RegisterMediaSinksObserver(_))
+  EXPECT_CALL(*router_, RegisterMediaSinksObserver(_))
       .Times(2)
       .WillRepeatedly(Return(true));
   EXPECT_TRUE(delegate_impl_->AddScreenAvailabilityListener(
@@ -277,7 +281,7 @@
       main_frame_process_id_, render_frame_id2, source2_.id()))
       << "Mapping not found for " << source2_.ToString();
 
-  EXPECT_CALL(router_, UnregisterMediaSinksObserver(_)).Times(2);
+  EXPECT_CALL(*router_, UnregisterMediaSinksObserver(_)).Times(2);
   delegate_impl_->RemoveScreenAvailabilityListener(
       main_frame_process_id_, main_frame_routing_id_, &listener1_);
   delegate_impl_->RemoveScreenAvailabilityListener(
@@ -289,9 +293,9 @@
 }
 
 TEST_F(PresentationServiceDelegateImplTest, AddMultipleListenersToFrame) {
-  ON_CALL(router_, RegisterMediaSinksObserver(_)).WillByDefault(Return(true));
+  ON_CALL(*router_, RegisterMediaSinksObserver(_)).WillByDefault(Return(true));
 
-  EXPECT_CALL(router_, RegisterMediaSinksObserver(_)).Times(2);
+  EXPECT_CALL(*router_, RegisterMediaSinksObserver(_)).Times(2);
   EXPECT_TRUE(delegate_impl_->AddScreenAvailabilityListener(
       main_frame_process_id_, main_frame_routing_id_, &listener1_));
   EXPECT_TRUE(delegate_impl_->AddScreenAvailabilityListener(
@@ -303,7 +307,7 @@
       main_frame_process_id_, main_frame_routing_id_, source2_.id()))
       << "Mapping not found for " << source2_.ToString();
 
-  EXPECT_CALL(router_, UnregisterMediaSinksObserver(_)).Times(2);
+  EXPECT_CALL(*router_, UnregisterMediaSinksObserver(_)).Times(2);
   delegate_impl_->RemoveScreenAvailabilityListener(
       main_frame_process_id_, main_frame_routing_id_, &listener1_);
   delegate_impl_->RemoveScreenAvailabilityListener(
@@ -315,7 +319,7 @@
 }
 
 TEST_F(PresentationServiceDelegateImplTest, AddSameListenerTwice) {
-  EXPECT_CALL(router_, RegisterMediaSinksObserver(_)).WillOnce(Return(true));
+  EXPECT_CALL(*router_, RegisterMediaSinksObserver(_)).WillOnce(Return(true));
   EXPECT_TRUE(delegate_impl_->AddScreenAvailabilityListener(
       main_frame_process_id_, main_frame_routing_id_, &listener1_));
   EXPECT_FALSE(delegate_impl_->AddScreenAvailabilityListener(
@@ -323,7 +327,7 @@
   EXPECT_TRUE(delegate_impl_->HasScreenAvailabilityListenerForTest(
       main_frame_process_id_, main_frame_routing_id_, source1_.id()));
 
-  EXPECT_CALL(router_, UnregisterMediaSinksObserver(_)).Times(1);
+  EXPECT_CALL(*router_, UnregisterMediaSinksObserver(_)).Times(1);
   delegate_impl_->RemoveScreenAvailabilityListener(
       main_frame_process_id_, main_frame_routing_id_, &listener1_);
   EXPECT_FALSE(delegate_impl_->HasScreenAvailabilityListenerForTest(
@@ -337,7 +341,7 @@
                   blink::mojom::ScreenAvailability::SOURCE_NOT_SUPPORTED));
   EXPECT_FALSE(delegate_impl_->AddScreenAvailabilityListener(
       main_frame_process_id_, main_frame_routing_id_, &listener));
-  EXPECT_CALL(router_, RegisterMediaSinksObserver(_)).Times(0);
+  EXPECT_CALL(*router_, RegisterMediaSinksObserver(_)).Times(0);
 }
 
 TEST_F(PresentationServiceDelegateImplTest, SetDefaultPresentationUrl) {
@@ -422,7 +426,7 @@
 
   // Set up a PresentationConnection so we can listen to it.
   std::vector<MediaRouteResponseCallback> route_response_callbacks;
-  EXPECT_CALL(router_, JoinRouteInternal(_, _, _, _, _, _, false))
+  EXPECT_CALL(*router_, JoinRouteInternal(_, _, _, _, _, _, false))
       .WillOnce(WithArgs<4>(
           Invoke([&route_response_callbacks](
                      std::vector<MediaRouteResponseCallback>& callbacks) {
@@ -458,21 +462,21 @@
       mock_callback;
   auto callback = mock_callback.Get();
   content::PresentationInfo connection(presentation_url1_, kPresentationId);
-  EXPECT_CALL(router_, OnAddPresentationConnectionStateChangedCallbackInvoked(
-                           Equals(callback)));
+  EXPECT_CALL(*router_, OnAddPresentationConnectionStateChangedCallbackInvoked(
+                            Equals(callback)));
   delegate_impl_->ListenForConnectionStateChange(
       main_frame_process_id_, main_frame_routing_id_, connection, callback);
 }
 
 TEST_F(PresentationServiceDelegateImplTest, Reset) {
-  EXPECT_CALL(router_, RegisterMediaSinksObserver(_))
+  EXPECT_CALL(*router_, RegisterMediaSinksObserver(_))
       .WillRepeatedly(Return(true));
 
   EXPECT_TRUE(delegate_impl_->AddScreenAvailabilityListener(
       main_frame_process_id_, main_frame_routing_id_, &listener1_));
   EXPECT_TRUE(delegate_impl_->HasScreenAvailabilityListenerForTest(
       main_frame_process_id_, main_frame_routing_id_, source1_.id()));
-  EXPECT_CALL(router_, UnregisterMediaSinksObserver(_)).Times(1);
+  EXPECT_CALL(*router_, UnregisterMediaSinksObserver(_)).Times(1);
   delegate_impl_->Reset(main_frame_process_id_, main_frame_routing_id_);
   EXPECT_FALSE(delegate_impl_->HasScreenAvailabilityListenerForTest(
       main_frame_process_id_, main_frame_routing_id_, source1_.id()));
@@ -481,7 +485,6 @@
 TEST_F(PresentationServiceDelegateImplTest, DelegateObservers) {
   std::unique_ptr<PresentationServiceDelegateImpl> manager(
       new PresentationServiceDelegateImpl(GetWebContents()));
-  manager->SetMediaRouterForTest(&router_);
 
   StrictMock<MockDelegateObserver> delegate_observer1;
   StrictMock<MockDelegateObserver> delegate_observer2;
@@ -497,7 +500,7 @@
 }
 
 TEST_F(PresentationServiceDelegateImplTest, SinksObserverCantRegister) {
-  EXPECT_CALL(router_, RegisterMediaSinksObserver(_)).WillOnce(Return(false));
+  EXPECT_CALL(*router_, RegisterMediaSinksObserver(_)).WillOnce(Return(false));
   EXPECT_CALL(listener1_, OnScreenAvailabilityChanged(
                               blink::mojom::ScreenAvailability::DISABLED));
   EXPECT_FALSE(delegate_impl_->AddScreenAvailabilityListener(
@@ -528,7 +531,7 @@
   EXPECT_CALL(mock_local_manager,
               UnregisterLocalPresentationController(kPresentationId, rfh_id))
       .Times(1);
-  EXPECT_CALL(router_, DetachRoute(_)).Times(0);
+  EXPECT_CALL(*router_, DetachRoute(_)).Times(0);
 
   delegate_impl_->CloseConnection(main_frame_process_id_,
                                   main_frame_routing_id_, kPresentationId);
@@ -590,7 +593,7 @@
 
   EXPECT_CALL(mock_local_manager,
               UnregisterLocalPresentationController(kPresentationId, rfh_id));
-  EXPECT_CALL(router_, DetachRoute(_)).Times(0);
+  EXPECT_CALL(*router_, DetachRoute(_)).Times(0);
   delegate_impl_->Reset(main_frame_process_id_, main_frame_routing_id_);
 }
 
@@ -616,13 +619,13 @@
       &mock_proxy, mojo::MakeRequest(&connection_ptr));
 
   content::PresentationConnectionRequest connection_request;
-  EXPECT_CALL(router_, RegisterRouteMessageObserver(_));
+  EXPECT_CALL(*router_, RegisterRouteMessageObserver(_));
   delegate_impl_->ConnectToPresentation(
       main_frame_process_id_, main_frame_routing_id_, presentation_info,
       std::move(connection_ptr), std::move(connection_request));
 
-  EXPECT_CALL(router_, UnregisterRouteMessageObserver(_));
-  EXPECT_CALL(router_, DetachRoute("route_id")).Times(1);
+  EXPECT_CALL(*router_, UnregisterRouteMessageObserver(_));
+  EXPECT_CALL(*router_, DetachRoute("route_id")).Times(1);
   delegate_impl_->Reset(main_frame_process_id_, main_frame_routing_id_);
 }
 
@@ -649,7 +652,7 @@
 
   // Auto-join requests should be rejected.
   EXPECT_CALL(mock_create_connection_callbacks, OnCreateConnectionError(_));
-  EXPECT_CALL(router_, JoinRouteInternal(_, kPresentationId, _, _, _, _, _))
+  EXPECT_CALL(*router_, JoinRouteInternal(_, kPresentationId, _, _, _, _, _))
       .Times(0);
   delegate_impl_->ReconnectPresentation(
       *presentation_request_, kPresentationId,
@@ -668,7 +671,7 @@
   }
 
   // Auto-join requests should now go through.
-  EXPECT_CALL(router_, JoinRouteInternal(_, kPresentationId, _, _, _, _, _))
+  EXPECT_CALL(*router_, JoinRouteInternal(_, kPresentationId, _, _, _, _, _))
       .Times(1);
   delegate_impl_->ReconnectPresentation(
       *presentation_request_, kPresentationId,
@@ -709,7 +712,7 @@
 
   // Auto-join requests should be rejected.
   EXPECT_CALL(mock_create_connection_callbacks, OnCreateConnectionError(_));
-  EXPECT_CALL(router_, JoinRouteInternal(_, kPresentationId, _, _, _, _, _))
+  EXPECT_CALL(*router_, JoinRouteInternal(_, kPresentationId, _, _, _, _, _))
       .Times(0);
   delegate_impl_->ReconnectPresentation(
       *presentation_request_, kPresentationId,
@@ -728,7 +731,7 @@
   }
 
   // Auto-join requests should now go through.
-  EXPECT_CALL(router_, JoinRouteInternal(_, kPresentationId, _, _, _, _, _))
+  EXPECT_CALL(*router_, JoinRouteInternal(_, kPresentationId, _, _, _, _, _))
       .Times(1);
   delegate_impl_->ReconnectPresentation(
       *presentation_request_, kPresentationId,
diff --git a/chrome/browser/media/router/providers/cast/dual_media_sink_service.cc b/chrome/browser/media/router/providers/cast/dual_media_sink_service.cc
index 7775626..81625f6 100644
--- a/chrome/browser/media/router/providers/cast/dual_media_sink_service.cc
+++ b/chrome/browser/media/router/providers/cast/dual_media_sink_service.cc
@@ -11,13 +11,24 @@
 
 namespace media_router {
 
+DualMediaSinkService* DualMediaSinkService::instance_for_test_ = nullptr;
+
 // static
 DualMediaSinkService* DualMediaSinkService::GetInstance() {
   DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
+  if (instance_for_test_)
+    return instance_for_test_;
+
   static DualMediaSinkService* instance = new DualMediaSinkService();
   return instance;
 }
 
+// static
+void DualMediaSinkService::SetInstanceForTest(
+    DualMediaSinkService* instance_for_test) {
+  instance_for_test_ = instance_for_test;
+}
+
 DualMediaSinkService::Subscription
 DualMediaSinkService::AddSinksDiscoveredCallback(
     const OnSinksDiscoveredProviderCallback& callback) {
diff --git a/chrome/browser/media/router/providers/cast/dual_media_sink_service.h b/chrome/browser/media/router/providers/cast/dual_media_sink_service.h
index f81befd..b51aa10d 100644
--- a/chrome/browser/media/router/providers/cast/dual_media_sink_service.h
+++ b/chrome/browser/media/router/providers/cast/dual_media_sink_service.h
@@ -44,6 +44,7 @@
 
   // Returns the lazily-created leaky singleton instance.
   static DualMediaSinkService* GetInstance();
+  static void SetInstanceForTest(DualMediaSinkService* instance_for_test);
 
   // Returns the current list of sinks, keyed by provider name.
   const base::flat_map<std::string, std::vector<MediaSinkInternal>>&
@@ -57,13 +58,20 @@
   Subscription AddSinksDiscoveredCallback(
       const OnSinksDiscoveredProviderCallback& callback);
 
-  void OnUserGesture();
+  virtual void OnUserGesture();
 
   // Starts mDNS discovery on |cast_media_sink_service_| if it is not already
   // started.
-  void StartMdnsDiscovery();
-  void RegisterMediaSinksObserver(MediaSinksObserver* observer);
-  void UnregisterMediaSinksObserver(MediaSinksObserver* observer);
+  virtual void StartMdnsDiscovery();
+  virtual void RegisterMediaSinksObserver(MediaSinksObserver* observer);
+  virtual void UnregisterMediaSinksObserver(MediaSinksObserver* observer);
+
+ protected:
+  // Used by tests.
+  DualMediaSinkService(
+      std::unique_ptr<CastMediaSinkService> cast_media_sink_service,
+      std::unique_ptr<DialMediaSinkService> dial_media_sink_service);
+  virtual ~DualMediaSinkService();
 
  private:
   friend class DualMediaSinkServiceTest;
@@ -75,15 +83,10 @@
 
   friend struct std::default_delete<DualMediaSinkService>;
 
+  static DualMediaSinkService* instance_for_test_;
+
   DualMediaSinkService();
 
-  // Used by tests.
-  DualMediaSinkService(
-      std::unique_ptr<CastMediaSinkService> cast_media_sink_service,
-      std::unique_ptr<DialMediaSinkService> dial_media_sink_service);
-
-  ~DualMediaSinkService();
-
   void OnSinksDiscovered(const std::string& provider_name,
                          std::vector<MediaSinkInternal> sinks);
 
diff --git a/chrome/browser/media/router/test/noop_dual_media_sink_service.cc b/chrome/browser/media/router/test/noop_dual_media_sink_service.cc
new file mode 100644
index 0000000..d4dcd61
--- /dev/null
+++ b/chrome/browser/media/router/test/noop_dual_media_sink_service.cc
@@ -0,0 +1,17 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/media/router/test/noop_dual_media_sink_service.h"
+
+#include "chrome/browser/media/router/discovery/dial/dial_media_sink_service.h"
+#include "chrome/browser/media/router/discovery/mdns/cast_media_sink_service.h"
+
+namespace media_router {
+
+NoopDualMediaSinkService::NoopDualMediaSinkService()
+    : DualMediaSinkService(std::unique_ptr<CastMediaSinkService>(nullptr),
+                           std::unique_ptr<DialMediaSinkService>(nullptr)) {}
+NoopDualMediaSinkService::~NoopDualMediaSinkService() = default;
+
+}  // namespace media_router
diff --git a/chrome/browser/media/router/test/noop_dual_media_sink_service.h b/chrome/browser/media/router/test/noop_dual_media_sink_service.h
new file mode 100644
index 0000000..d8f30d4
--- /dev/null
+++ b/chrome/browser/media/router/test/noop_dual_media_sink_service.h
@@ -0,0 +1,29 @@
+// 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 CHROME_BROWSER_MEDIA_ROUTER_TEST_NOOP_DUAL_MEDIA_SINK_SERVICE_H_
+#define CHROME_BROWSER_MEDIA_ROUTER_TEST_NOOP_DUAL_MEDIA_SINK_SERVICE_H_
+
+#include "chrome/browser/media/router/providers/cast/dual_media_sink_service.h"
+
+namespace media_router {
+
+class NoopDualMediaSinkService : public DualMediaSinkService {
+ public:
+  NoopDualMediaSinkService();
+  ~NoopDualMediaSinkService() override;
+
+  // DualMediaSinkService
+  void OnUserGesture() override {}
+  void StartMdnsDiscovery() override {}
+  void RegisterMediaSinksObserver(MediaSinksObserver* observer) override {}
+  void UnregisterMediaSinksObserver(MediaSinksObserver* observer) override {}
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(NoopDualMediaSinkService);
+};
+
+}  // namespace media_router
+
+#endif  // CHROME_BROWSER_MEDIA_ROUTER_TEST_NOOP_DUAL_MEDIA_SINK_SERVICE_H_
diff --git a/chrome/browser/media/webrtc/desktop_capture_access_handler.cc b/chrome/browser/media/webrtc/desktop_capture_access_handler.cc
index cbb52d3..28732424 100644
--- a/chrome/browser/media/webrtc/desktop_capture_access_handler.cc
+++ b/chrome/browser/media/webrtc/desktop_capture_access_handler.cc
@@ -39,6 +39,7 @@
 
 #if defined(OS_CHROMEOS)
 #include "ash/shell.h"
+#include "chrome/browser/chromeos/ash_config.h"
 #endif  // defined(OS_CHROMEOS)
 
 using content::BrowserThread;
@@ -270,6 +271,15 @@
   content::MediaStreamRequestResult result =
       content::MEDIA_DEVICE_INVALID_STATE;
 
+#if defined(OS_CHROMEOS)
+  if (chromeos::GetAshConfig() == ash::Config::MASH) {
+    // TODO(crbug.com/806366): Screen capture support for mash.
+    NOTIMPLEMENTED() << "Screen capture not yet implemented in --mash";
+    screen_capture_enabled = false;
+    result = content::MEDIA_DEVICE_NOT_SUPPORTED;
+  }
+#endif  // defined(OS_CHROMEOS)
+
   // Approve request only when the following conditions are met:
   //  1. Screen capturing is enabled via command line switch or white-listed for
   //     the given origin.
diff --git a/chrome/browser/mouseleave_browsertest.cc b/chrome/browser/mouseleave_browsertest.cc
index d906a8ee2..02c981e 100644
--- a/chrome/browser/mouseleave_browsertest.cc
+++ b/chrome/browser/mouseleave_browsertest.cc
@@ -82,8 +82,9 @@
   DISALLOW_COPY_AND_ASSIGN(MouseLeaveTest);
 };
 
-#if defined(OS_MACOSX)
+#if defined(OS_MACOSX) || defined(OS_LINUX)
 // OS_MACOSX: Missing automation provider support: http://crbug.com/45892.
+// OS_LINUX: http://crbug.com/133361.
 #define MAYBE_TestOnMouseOut DISABLED_TestOnMouseOut
 #elif defined(OS_WIN) && !defined(NDEBUG)
 // Flaky on Win debug; see https://crbug.com/419468.
@@ -96,20 +97,18 @@
   MouseLeaveTestCommon();
 }
 
-#if defined(OS_MACOSX)
-// OS_MACOSX: Missing automation provider support: http://crbug.com/45892.
-#define MAYBE_MouseDownOnBrowserCaption DISABLED_MouseDownOnBrowserCaption
-#else
-#define MAYBE_MouseDownOnBrowserCaption MouseDownOnBrowserCaption
-#endif
-
-IN_PROC_BROWSER_TEST_F(MouseLeaveTest, MAYBE_MouseDownOnBrowserCaption) {
+#if defined(OS_WIN)
+// For MAC: Missing automation provider support: http://crbug.com/45892
+// For linux : http://crbug.com/133361. interactive mouse tests are flaky.
+IN_PROC_BROWSER_TEST_F(MouseLeaveTest, MouseDownOnBrowserCaption) {
   gfx::Rect browser_bounds = browser()->window()->GetBounds();
-  ui_controls::SendMouseMove(browser_bounds.x() + 180, browser_bounds.y() + 10);
+  ui_controls::SendMouseMove(browser_bounds.x() + 200,
+                             browser_bounds.y() + 10);
   ui_controls::SendMouseClick(ui_controls::LEFT);
 
   MouseLeaveTestCommon();
 }
+#endif
 
 #if defined(OS_MACOSX) || defined(OS_WIN)
 // Test that a mouseleave is not triggered when showing the context menu.
diff --git a/chrome/browser/notifications/platform_notification_service_interactive_uitest.cc b/chrome/browser/notifications/platform_notification_service_interactive_uitest.cc
index 106fa3d..4403111 100644
--- a/chrome/browser/notifications/platform_notification_service_interactive_uitest.cc
+++ b/chrome/browser/notifications/platform_notification_service_interactive_uitest.cc
@@ -985,3 +985,30 @@
   // notification should be shown without an image.
   EXPECT_TRUE(notifications[0].image().IsEmpty());
 }
+
+class PlatformNotificationServiceMojoEnabledBrowserTest
+    : public PlatformNotificationServiceBrowserTest {
+ public:
+  // InProcessBrowserTest overrides.
+  void SetUpInProcessBrowserTestFixture() override {
+    scoped_feature_list_.InitWithFeatures({features::kNotificationsWithMojo},
+                                          {});
+  }
+
+ private:
+  base::test::ScopedFeatureList scoped_feature_list_;
+};
+
+IN_PROC_BROWSER_TEST_F(PlatformNotificationServiceMojoEnabledBrowserTest,
+                       DisplayPersistentNotificationWithPermission) {
+  RequestAndAcceptPermission();
+
+  std::string script_result;
+  ASSERT_TRUE(RunScript("DisplayPersistentNotification('action_none')",
+                        &script_result));
+  EXPECT_EQ("ok", script_result);
+
+  std::vector<message_center::Notification> notifications =
+      GetDisplayedNotifications(true /* is_persistent */);
+  ASSERT_EQ(1u, notifications.size());
+}
diff --git a/chrome/browser/password_manager/password_manager_browsertest.cc b/chrome/browser/password_manager/password_manager_browsertest.cc
index 53af5a6..1616e7e 100644
--- a/chrome/browser/password_manager/password_manager_browsertest.cc
+++ b/chrome/browser/password_manager/password_manager_browsertest.cc
@@ -1904,8 +1904,16 @@
   EXPECT_FALSE(prompt_observer->IsSavePromptShownAutomatically());
 }
 
+// https://crbug.com/814845 Flaky on macOS ASan.
+#if defined(ADDRESS_SANITIZER) && defined(OS_MACOSX)
+#define MAYBE_InFrameNavigationDoesNotClearPopupState \
+  DISABLED_InFrameNavigationDoesNotClearPopupState
+#else
+#define MAYBE_InFrameNavigationDoesNotClearPopupState \
+  InFrameNavigationDoesNotClearPopupState
+#endif
 IN_PROC_BROWSER_TEST_F(PasswordManagerBrowserTestBase,
-                       InFrameNavigationDoesNotClearPopupState) {
+                       MAYBE_InFrameNavigationDoesNotClearPopupState) {
   scoped_refptr<password_manager::TestPasswordStore> password_store =
       static_cast<password_manager::TestPasswordStore*>(
           PasswordStoreFactory::GetForProfile(
diff --git a/chrome/browser/policy/policy_browsertest.cc b/chrome/browser/policy/policy_browsertest.cc
index bf485c2..cf10c672 100644
--- a/chrome/browser/policy/policy_browsertest.cc
+++ b/chrome/browser/policy/policy_browsertest.cc
@@ -71,7 +71,6 @@
 #include "chrome/browser/prefs/session_startup_pref.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/safe_browsing/chrome_password_protection_service.h"
-#include "chrome/browser/search/search.h"
 #include "chrome/browser/search_engines/template_url_service_factory.h"
 #include "chrome/browser/ssl/ssl_blocking_page.h"
 #include "chrome/browser/task_manager/task_manager_interface.h"
@@ -2298,7 +2297,7 @@
   UpdateProviderPolicy(policies);
   EXPECT_TRUE(chrome::ExecuteCommand(browser(), IDC_HOME));
   content::WaitForLoadStop(contents);
-  EXPECT_TRUE(search::IsInstantNTP(contents));
+  EXPECT_EQ(GURL(chrome::kChromeUINewTabURL), contents->GetURL());
 }
 
 #if defined(OS_MACOSX) && defined(ADDRESS_SANITIZER)
diff --git a/chrome/browser/printing/pdf_to_emf_converter.cc b/chrome/browser/printing/pdf_to_emf_converter.cc
index 498df2b..4715ec3 100644
--- a/chrome/browser/printing/pdf_to_emf_converter.cc
+++ b/chrome/browser/printing/pdf_to_emf_converter.cc
@@ -212,6 +212,14 @@
                    StartCallback start_callback);
   ~PdfConverterImpl() override;
 
+  static void set_fail_when_creating_temp_file_for_tests(bool fail) {
+    simulate_failure_creating_temp_file_ = fail;
+  }
+
+  static bool fail_when_creating_temp_file_for_tests() {
+    return simulate_failure_creating_temp_file_;
+  }
+
  private:
   class GetPageCallbackData {
    public:
@@ -287,9 +295,14 @@
 
   base::WeakPtrFactory<PdfConverterImpl> weak_ptr_factory_;
 
+  static bool simulate_failure_creating_temp_file_;
+
   DISALLOW_COPY_AND_ASSIGN(PdfConverterImpl);
 };
 
+// static
+bool PdfConverterImpl::simulate_failure_creating_temp_file_ = false;
+
 std::unique_ptr<MetafilePlayer> PdfConverterImpl::GetFileFromTemp(
     ScopedTempFile temp_file) {
   if (settings_.mode == PdfRenderSettings::Mode::POSTSCRIPT_LEVEL2 ||
@@ -302,6 +315,9 @@
 }
 
 ScopedTempFile CreateTempFile(scoped_refptr<RefCountedTempDir>* temp_dir) {
+  if (PdfConverterImpl::fail_when_creating_temp_file_for_tests())
+    return ScopedTempFile();
+
   if (!temp_dir->get())
     *temp_dir = base::MakeRefCounted<RefCountedTempDir>();
   ScopedTempFile file;
@@ -540,7 +556,7 @@
   LOG(ERROR) << "Failed to convert PDF: " << error_message;
   base::WeakPtr<PdfConverterImpl> weak_this = weak_ptr_factory_.GetWeakPtr();
   if (!start_callback_.is_null()) {
-    OnPageCount(mojom::PdfToEmfConverterPtr(), 0);
+    std::move(start_callback_).Run(/*page_count=*/0);
     if (!weak_this)
       return;  // Protect against the |start_callback_| deleting |this|.
   }
@@ -570,4 +586,14 @@
                                             std::move(start_callback));
 }
 
+ScopedSimulateFailureCreatingTempFileForTests::
+    ScopedSimulateFailureCreatingTempFileForTests() {
+  PdfConverterImpl::set_fail_when_creating_temp_file_for_tests(true);
+}
+
+ScopedSimulateFailureCreatingTempFileForTests::
+    ~ScopedSimulateFailureCreatingTempFileForTests() {
+  PdfConverterImpl::set_fail_when_creating_temp_file_for_tests(false);
+}
+
 }  // namespace printing
diff --git a/chrome/browser/printing/pdf_to_emf_converter.h b/chrome/browser/printing/pdf_to_emf_converter.h
index 1263c44..5aa61f7 100644
--- a/chrome/browser/printing/pdf_to_emf_converter.h
+++ b/chrome/browser/printing/pdf_to_emf_converter.h
@@ -38,6 +38,16 @@
   virtual void GetPage(int page_number,
                        const GetPageCallback& get_page_callback) = 0;
 };
+
+// Object used by tests to exercise the temporary file creation failure code
+// path. As long as this object is alive, the PdfConverter will fail when
+// creating temporary files.
+class ScopedSimulateFailureCreatingTempFileForTests {
+ public:
+  ScopedSimulateFailureCreatingTempFileForTests();
+  ~ScopedSimulateFailureCreatingTempFileForTests();
+};
+
 }  // namespace printing
 
 #endif  // CHROME_BROWSER_PRINTING_PDF_TO_EMF_CONVERTER_H_
diff --git a/chrome/browser/printing/pdf_to_emf_converter_browsertest.cc b/chrome/browser/printing/pdf_to_emf_converter_browsertest.cc
index 03c406e4..db7992c 100644
--- a/chrome/browser/printing/pdf_to_emf_converter_browsertest.cc
+++ b/chrome/browser/printing/pdf_to_emf_converter_browsertest.cc
@@ -79,7 +79,19 @@
 
 }  // namespace
 
-IN_PROC_BROWSER_TEST_F(PDFToEMFConverterBrowserTest, TestFailure) {
+IN_PROC_BROWSER_TEST_F(PDFToEMFConverterBrowserTest, TestFailureNoTempFile) {
+  ScopedSimulateFailureCreatingTempFileForTests fail_creating_temp_file;
+
+  base::RunLoop run_loop;
+  int page_count = -1;
+  std::unique_ptr<PdfConverter> pdf_converter = PdfConverter::StartPdfConverter(
+      base::MakeRefCounted<base::RefCountedStaticMemory>(), PdfRenderSettings(),
+      base::Bind(&StartCallbackImpl, run_loop.QuitClosure(), &page_count));
+  run_loop.Run();
+  EXPECT_EQ(0, page_count);
+}
+
+IN_PROC_BROWSER_TEST_F(PDFToEMFConverterBrowserTest, TestFailureBadPdf) {
   scoped_refptr<base::RefCountedStaticMemory> bad_pdf_data =
       base::MakeRefCounted<base::RefCountedStaticMemory>("0123456789", 10);
 
diff --git a/chrome/browser/profiles/profile_io_data.cc b/chrome/browser/profiles/profile_io_data.cc
index 1b1d0e8..2118d08 100644
--- a/chrome/browser/profiles/profile_io_data.cc
+++ b/chrome/browser/profiles/profile_io_data.cc
@@ -126,6 +126,9 @@
 
 #if defined(OS_ANDROID)
 #include "content/public/browser/android/content_protocol_handler.h"
+#else
+#include "chrome/browser/ui/search/new_tab_page_interceptor_service.h"
+#include "chrome/browser/ui/search/new_tab_page_interceptor_service_factory.h"
 #endif  // defined(OS_ANDROID)
 
 #if defined(OS_CHROMEOS)
@@ -454,6 +457,15 @@
   params->protocol_handler_interceptor =
       protocol_handler_registry->CreateJobInterceptorFactory();
 
+#if !defined(OS_ANDROID)
+  NewTabPageInterceptorService* new_tab_interceptor_service =
+      NewTabPageInterceptorServiceFactory::GetForProfile(profile);
+  if (new_tab_interceptor_service) {
+    params->new_tab_page_interceptor =
+        new_tab_interceptor_service->CreateInterceptor();
+  }
+#endif
+
 #if defined(OS_CHROMEOS)
   // Enable client certificates for the Chrome OS sign-in frame, if this feature
   // is not disabled by a flag.
diff --git a/chrome/browser/profiles/profile_manager.cc b/chrome/browser/profiles/profile_manager.cc
index c5ab3d4..dcd20d5 100644
--- a/chrome/browser/profiles/profile_manager.cc
+++ b/chrome/browser/profiles/profile_manager.cc
@@ -490,8 +490,13 @@
 // static
 Profile* ProfileManager::CreateInitialProfile() {
   ProfileManager* const profile_manager = g_browser_process->profile_manager();
-  return profile_manager->GetProfile(profile_manager->user_data_dir().Append(
-      profile_manager->GetInitialProfileDir()));
+  Profile* profile =
+      profile_manager->GetProfile(profile_manager->user_data_dir().Append(
+          profile_manager->GetInitialProfileDir()));
+
+  if (profile_manager->ShouldGoOffTheRecord(profile))
+    return profile->GetOffTheRecordProfile();
+  return profile;
 }
 
 Profile* ProfileManager::GetProfile(const base::FilePath& profile_dir) {
diff --git a/chrome/browser/resources/chromeos/wallpaper_manager/js/wallpaper_manager.js b/chrome/browser/resources/chromeos/wallpaper_manager/js/wallpaper_manager.js
index e44cae27..c7bd27d 100644
--- a/chrome/browser/resources/chromeos/wallpaper_manager/js/wallpaper_manager.js
+++ b/chrome/browser/resources/chromeos/wallpaper_manager/js/wallpaper_manager.js
@@ -1028,7 +1028,7 @@
           var xhr = new XMLHttpRequest();
           xhr.open(
               'GET',
-              self.dataItem.baseURL +
+              selectedItem.baseURL +
                   WallpaperUtil.getOnlineWallpaperThumbnailSuffix(),
               true);
           xhr.responseType = 'arraybuffer';
diff --git a/chrome/browser/resources/md_downloads/downloads.html b/chrome/browser/resources/md_downloads/downloads.html
index bc126bb..07eaf62 100644
--- a/chrome/browser/resources/md_downloads/downloads.html
+++ b/chrome/browser/resources/md_downloads/downloads.html
@@ -9,6 +9,9 @@
       --downloads-card-margin: 24px;
       --downloads-card-width: 680px;
       background: #f1f1f1;
+
+      /* Remove 300ms delay for 'click' event, when using touch interface. */
+      touch-action: manipulation;
     }
 
     .loading {
diff --git a/chrome/browser/resources/md_downloads/item.html b/chrome/browser/resources/md_downloads/item.html
index 6953019..3696956 100644
--- a/chrome/browser/resources/md_downloads/item.html
+++ b/chrome/browser/resources/md_downloads/item.html
@@ -244,7 +244,7 @@
         <div id="title-area"><!--
           Can't have any line breaks.
           --><a is="action-link" id="file-link" href="[[data.url]]"
-              on-tap="onFileLinkTap_"
+              on-click="onFileLinkTap_"
               hidden="[[!completelyOnDisk_]]">[[data.file_name]]</a><!--
           Before #name.
           --><span id="name"
@@ -263,20 +263,20 @@
         </template>
 
         <div id="safe" class="controls" hidden="[[isDangerous_]]">
-          <a is="action-link" id="show" on-tap="onShowTap_"
+          <a is="action-link" id="show" on-click="onShowTap_"
               hidden="[[!completelyOnDisk_]]">$i18n{controlShowInFolder}</a>
           <template is="dom-if" if="[[data.retry]]">
-            <paper-button id="retry" on-tap="onRetryTap_">
+            <paper-button id="retry" on-click="onRetryTap_">
               $i18n{controlRetry}
             </paper-button>
           </template>
           <template is="dom-if" if="[[pauseOrResumeText_]]">
-            <paper-button id="pause-or-resume" on-tap="onPauseOrResumeTap_">
+            <paper-button id="pause-or-resume" on-click="onPauseOrResumeTap_">
               [[pauseOrResumeText_]]
             </paper-button>
           </template>
           <template is="dom-if" if="[[showCancel_]]">
-            <paper-button id="cancel" on-tap="onCancelTap_">
+            <paper-button id="cancel" on-click="onCancelTap_">
               $i18n{controlCancel}
             </paper-button>
           </template>
@@ -287,17 +287,17 @@
           <div id="dangerous" class="controls">
             <!-- Dangerous file types (e.g. .exe, .jar). -->
             <template is="dom-if" if="[[!isMalware_]]">
-              <paper-button id="discard" on-tap="onDiscardDangerousTap_"
+              <paper-button id="discard" on-click="onDiscardDangerousTap_"
                   class="discard">$i18n{dangerDiscard}</paper-button>
-              <paper-button id="save" on-tap="onSaveDangerousTap_"
+              <paper-button id="save" on-click="onSaveDangerousTap_"
                   class="keep">$i18n{dangerSave}</paper-button>
             </template>
 
             <!-- Things that safe browsing has determined to be dangerous. -->
             <template is="dom-if" if="[[isMalware_]]">
-              <paper-button id="danger-remove" on-tap="onDiscardDangerousTap_"
+              <paper-button id="danger-remove" on-click="onDiscardDangerousTap_"
                   class="discard">$i18n{controlRemoveFromList}</paper-button>
-              <paper-button id="restore" on-tap="onSaveDangerousTap_"
+              <paper-button id="restore" on-click="onSaveDangerousTap_"
                   class="keep">$i18n{dangerRestore}</paper-button>
             </template>
           </div>
@@ -309,7 +309,7 @@
             title="$i18n{controlRemoveFromList}"
             aria-label="$i18n{controlRemoveFromList}"
             style$="[[computeRemoveStyle_(isDangerous_, showCancel_)]]"
-            on-tap="onRemoveTap_">&#x2715;</button>
+            on-click="onRemoveTap_">&#x2715;</button>
       </div>
 
       <div id="incognito" title="$i18n{inIncognito}" hidden="[[!data.otr]]">
diff --git a/chrome/browser/resources/md_downloads/toolbar.html b/chrome/browser/resources/md_downloads/toolbar.html
index da493491..a98f6b0 100644
--- a/chrome/browser/resources/md_downloads/toolbar.html
+++ b/chrome/browser/resources/md_downloads/toolbar.html
@@ -41,17 +41,17 @@
         spinner-active="{{spinnerActive}}" on-search-changed="onSearchChanged_">
       <button is="paper-icon-button-light" id="moreActions"
           title="$i18n{moreActions}" class="dropdown-trigger"
-          on-tap="onMoreActionsTap_">
+          on-click="onMoreActionsTap_">
         <iron-icon icon="cr:more-vert"></iron-icon>
       </button>
     </cr-toolbar>
     <dialog is="cr-action-menu" id="moreActionsMenu">
       <button slot="item" class="dropdown-item clear-all"
-          on-tap="onClearAllTap_">
+          on-click="onClearAllTap_">
         $i18n{clearAll}
       </button>
       <button slot="item" class="dropdown-item"
-          on-tap="onOpenDownloadsFolderTap_">
+          on-click="onOpenDownloadsFolderTap_">
         $i18n{openDownloadsFolder}
       </button>
     </dialog>
diff --git a/chrome/browser/resources/md_history/history.html b/chrome/browser/resources/md_history/history.html
index d98bd50..bdce86e 100644
--- a/chrome/browser/resources/md_history/history.html
+++ b/chrome/browser/resources/md_history/history.html
@@ -8,6 +8,11 @@
   <link rel="stylesheet" href="chrome://resources/css/md_colors.css">
 
   <style>
+    html {
+      /* Remove 300ms delay for 'click' event, when using touch interface. */
+      touch-action: manipulation;
+    }
+
     html,
     body {
       height: 100%;
diff --git a/chrome/browser/resources/md_history/history_item.js b/chrome/browser/resources/md_history/history_item.js
index fa0dd0ec..3329aa1 100644
--- a/chrome/browser/resources/md_history/history_item.js
+++ b/chrome/browser/resources/md_history/history_item.js
@@ -278,13 +278,13 @@
         item: this.item,
       });
 
-      // Stops the 'tap' event from closing the menu when it opens.
+      // Stops the 'click' event from closing the menu when it opens.
       e.stopPropagation();
     },
 
     /**
-     * Record metrics when a result is clicked. This is deliberately tied to
-     * on-click rather than on-tap, as on-click triggers from middle clicks.
+     * Record metrics when a result is clicked.
+     * @private
      */
     onLinkClick_: function() {
       const browserService = md_history.BrowserService.getInstance();
diff --git a/chrome/browser/resources/md_history/history_list.html b/chrome/browser/resources/md_history/history_list.html
index 81b7d12..ed46d9f 100644
--- a/chrome/browser/resources/md_history/history_list.html
+++ b/chrome/browser/resources/md_history/history_list.html
@@ -63,10 +63,10 @@
         <div slot="title">$i18n{removeSelected}</div>
         <div slot="body">$i18n{deleteWarning}</div>
         <div slot="button-container">
-          <paper-button class="cancel-button" on-tap="onDialogCancelTap_">
+          <paper-button class="cancel-button" on-click="onDialogCancelTap_">
             $i18n{cancel}
           </paper-button>
-          <paper-button class="action-button" on-tap="onDialogConfirmTap_">
+          <paper-button class="action-button" on-click="onDialogConfirmTap_">
             $i18n{deleteConfirm}
           </paper-button>
         </div>
@@ -78,12 +78,12 @@
         <button id="menuMoreButton" slot="item" class="dropdown-item"
             hidden="[[!canSearchMoreFromSite_(
                 searchedTerm, actionMenuModel_.item.domain)]]"
-            on-tap="onMoreFromSiteTap_">
+            on-click="onMoreFromSiteTap_">
           $i18n{moreFromSite}
         </button>
         <button id="menuRemoveButton" slot="item" class="dropdown-item"
             hidden="[[!canDeleteHistory_]]"
-            on-tap="onRemoveFromHistoryTap_">
+            on-click="onRemoveFromHistoryTap_">
           $i18n{removeFromHistory}
         </button>
       </dialog>
diff --git a/chrome/browser/resources/md_history/side_bar.html b/chrome/browser/resources/md_history/side_bar.html
index b49c3e2fe..49b1184 100644
--- a/chrome/browser/resources/md_history/side_bar.html
+++ b/chrome/browser/resources/md_history/side_bar.html
@@ -107,7 +107,7 @@
       <div class="separator"></div>
       <a id="clear-browsing-data"
           href="chrome://settings/clearBrowserData"
-          on-tap="onClearBrowsingDataTap_"
+          on-click="onClearBrowsingDataTap_"
           disabled$="[[guestSession_]]"
           tabindex$="[[computeClearBrowsingDataTabIndex_(guestSession_)]]">
         $i18n{clearBrowsingData}
diff --git a/chrome/browser/resources/md_history/synced_device_card.js b/chrome/browser/resources/md_history/synced_device_card.js
index 3206eb2..bbd109a 100644
--- a/chrome/browser/resources/md_history/synced_device_card.js
+++ b/chrome/browser/resources/md_history/synced_device_card.js
@@ -68,8 +68,7 @@
   },
 
   /**
-   * Open a single synced tab. Listens to 'click' rather than 'tap'
-   * to determine what modifier keys were pressed.
+   * Open a single synced tab.
    * @param {DomRepeatClickEvent} e
    * @private
    */
diff --git a/chrome/browser/resources/md_history/synced_device_manager.html b/chrome/browser/resources/md_history/synced_device_manager.html
index 0b99a74..7b10c59 100644
--- a/chrome/browser/resources/md_history/synced_device_manager.html
+++ b/chrome/browser/resources/md_history/synced_device_manager.html
@@ -82,7 +82,7 @@
       <div id="sign-in-promo">$i18n{signInPromo}</div>
       <div id="sign-in-promo-desc">$i18n{signInPromoDesc}</div>
       <paper-button id="sign-in-button" class="action-button"
-          on-tap="onSignInTap_">
+          on-click="onSignInTap_">
         $i18n{signInButton}
       </paper-button>
     </div>
@@ -90,11 +90,11 @@
     <template is="cr-lazy-render" id="menu">
       <dialog is="cr-action-menu">
         <button id="menuOpenButton" slot="item" class="dropdown-item"
-            on-tap="onOpenAllTap_">
+            on-click="onOpenAllTap_">
           $i18n{openAll}
         </button>
         <button id="menuDeleteButton" slot="item" class="dropdown-item"
-            on-tap="onDeleteSessionTap_">
+            on-click="onDeleteSessionTap_">
           $i18n{deleteSession}
         </button>
       </dialog>
diff --git a/chrome/browser/resources/print_preview/print_preview_utils.js b/chrome/browser/resources/print_preview/print_preview_utils.js
index cd93198..0220810f 100644
--- a/chrome/browser/resources/print_preview/print_preview_utils.js
+++ b/chrome/browser/resources/print_preview/print_preview_utils.js
@@ -114,7 +114,7 @@
       opt_totalPageCount ? opt_totalPageCount : MAX_PAGE_NUMBER;
 
   const regex = /^\s*([0-9]*)\s*-\s*([0-9]*)\s*$/;
-  const parts = pageRangeText.split(/,/);
+  const parts = pageRangeText.split(/,|\u3001/);
 
   const pageRanges = [];
   for (let i = 0; i < parts.length; ++i) {
diff --git a/chrome/browser/resources/print_preview/print_preview_utils_unittest.gtestjs b/chrome/browser/resources/print_preview/print_preview_utils_unittest.gtestjs
index ff15d6f..17fa25be 100644
--- a/chrome/browser/resources/print_preview/print_preview_utils_unittest.gtestjs
+++ b/chrome/browser/resources/print_preview/print_preview_utils_unittest.gtestjs
@@ -86,6 +86,12 @@
                     pageRangeTextToPageRanges("-", null));
   assertRangesEqual([[1, 1000000000]],
                     pageRangeTextToPageRanges("-", 0));
+
+  // https://crbug.com/806165
+  assertRangesEqual([1, 2, 3, 1, 56],
+                    pageRangeTextToPageRanges("1\u30012\u30013\u30011\u300156", 100));
+  assertRangesEqual([1, 2, 3, 1, 56],
+                    pageRangeTextToPageRanges("1,2,3\u30011\u300156", 100));
 });
 
 TEST_F('PrintPreviewUtilsUnitTest', 'InvalidPageRanges', function() {
@@ -101,6 +107,11 @@
       pageRangeTextToPageRanges("1,2,56-40", 100));
   assertEquals(PageRangeStatus.LIMIT_ERROR,
       pageRangeTextToPageRanges("101-110", 100));
+
+  assertEquals(PageRangeStatus.SYNTAX_ERROR,
+      pageRangeTextToPageRanges("1\u30012\u30010\u300156", 100));
+  assertEquals(PageRangeStatus.SYNTAX_ERROR,
+      pageRangeTextToPageRanges("-1,1,2\u3001\u300156", 100));
 });
 
 TEST_F('PrintPreviewUtilsUnitTest', 'PageRangeTextToPageList', function() {
diff --git a/chrome/browser/sessions/persistent_tab_restore_service_unittest.cc b/chrome/browser/sessions/persistent_tab_restore_service_unittest.cc
index efca2921..9489943 100644
--- a/chrome/browser/sessions/persistent_tab_restore_service_unittest.cc
+++ b/chrome/browser/sessions/persistent_tab_restore_service_unittest.cc
@@ -15,7 +15,6 @@
 #include "base/strings/string_number_conversions.h"
 #include "base/strings/stringprintf.h"
 #include "base/strings/utf_string_conversions.h"
-#include "base/test/bind_test_util.h"
 #include "chrome/browser/chrome_notification_types.h"
 #include "chrome/browser/sessions/chrome_tab_restore_service_client.h"
 #include "chrome/browser/sessions/session_service.h"
@@ -41,7 +40,6 @@
 #include "content/public/test/web_contents_tester.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
-typedef sessions::TabRestoreService::Entry Entry;
 typedef sessions::TabRestoreService::Tab Tab;
 typedef sessions::TabRestoreService::Window Window;
 
@@ -206,6 +204,8 @@
   bool got_loaded() const { return got_loaded_; }
 
   // TabRestoreServiceObserver:
+  void TabRestoreServiceChanged(sessions::TabRestoreService* service) override {
+  }
   void TabRestoreServiceDestroyed(
       sessions::TabRestoreService* service) override {}
   void TabRestoreServiceLoaded(sessions::TabRestoreService* service) override {
@@ -231,7 +231,7 @@
   ASSERT_EQ(1U, service_->entries().size());
 
   // Make sure the entry matches.
-  Entry* entry = service_->entries().front().get();
+  sessions::TabRestoreService::Entry* entry = service_->entries().front().get();
   ASSERT_EQ(sessions::TabRestoreService::TAB, entry->type);
   Tab* tab = static_cast<Tab*>(entry);
   EXPECT_FALSE(tab->pinned);
@@ -290,7 +290,8 @@
   ASSERT_EQ(1U, service_->entries().size());
 
   // And verify the entry.
-  Entry* entry = service_->entries().front().get();
+  sessions::PersistentTabRestoreService::Entry* entry =
+      service_->entries().front().get();
   ASSERT_EQ(sessions::TabRestoreService::TAB, entry->type);
   Tab* tab = static_cast<Tab*>(entry);
   EXPECT_FALSE(tab->pinned);
@@ -315,7 +316,7 @@
 
   // We have to explicitly mark the tab as pinned as there is no browser for
   // these tests.
-  Entry* entry = service_->entries().front().get();
+  sessions::TabRestoreService::Entry* entry = service_->entries().front().get();
   ASSERT_EQ(sessions::TabRestoreService::TAB, entry->type);
   Tab* tab = static_cast<Tab*>(entry);
   tab->pinned = true;
@@ -341,91 +342,6 @@
   EXPECT_TRUE(extension_app_id == tab->extension_app_id);
 }
 
-// Tests deleting entries.
-TEST_F(PersistentTabRestoreServiceTest, DeleteNavigationEntries) {
-  SynchronousLoadTabsFromLastSession();
-  AddThreeNavigations();
-
-  // Have the service record the tab.
-  service_->CreateHistoricalTab(live_tab(), -1);
-
-  service_->DeleteNavigationEntries(
-      base::BindLambdaForTesting([&](const SerializedNavigationEntry& entry) {
-        return entry.virtual_url() == url2_;
-      }));
-
-  // The entry should still exist but url2_ was removed and indices adjusted.
-  ASSERT_EQ(1U, service_->entries().size());
-  Entry* entry = service_->entries().front().get();
-  ASSERT_EQ(sessions::TabRestoreService::TAB, entry->type);
-  Tab* tab = static_cast<Tab*>(entry);
-  ASSERT_EQ(2U, tab->navigations.size());
-  EXPECT_EQ(url1_, tab->navigations[0].virtual_url());
-  EXPECT_EQ(0, tab->navigations[0].index());
-  EXPECT_EQ(url3_, tab->navigations[1].virtual_url());
-  EXPECT_EQ(1, tab->navigations[1].index());
-  EXPECT_EQ(1, tab->current_navigation_index);
-
-  service_->DeleteNavigationEntries(base::BindRepeating(
-      [](const SerializedNavigationEntry& entry) { return true; }));
-
-  // The entry should be removed.
-  EXPECT_EQ(0U, service_->entries().size());
-}
-
-// Tests deleting entries.
-TEST_F(PersistentTabRestoreServiceTest, DeleteCurrentEntry) {
-  SynchronousLoadTabsFromLastSession();
-  AddThreeNavigations();
-
-  // Have the service record the tab.
-  service_->CreateHistoricalTab(live_tab(), -1);
-
-  service_->DeleteNavigationEntries(
-      base::BindLambdaForTesting([&](const SerializedNavigationEntry& entry) {
-        return entry.virtual_url() == url3_;
-      }));
-
-  // The entry should be deleted because the current url was deleted.
-  EXPECT_EQ(0U, service_->entries().size());
-}
-
-// Tests deleting entries.
-TEST_F(PersistentTabRestoreServiceTest, DeleteEntriesAndRecreate) {
-  SynchronousLoadTabsFromLastSession();
-  AddThreeNavigations();
-
-  // Have the service record the tab.
-  service_->CreateHistoricalTab(live_tab(), -1);
-
-  // Delete the navigation for url2_.
-  service_->DeleteNavigationEntries(
-      base::BindLambdaForTesting([&](const SerializedNavigationEntry& entry) {
-        return entry.virtual_url() == url2_;
-      }));
-  // Recreate the service and have it load the tabs.
-  RecreateService();
-  // The entry should still exist but url2_ was removed and indices adjusted.
-  ASSERT_EQ(1U, service_->entries().size());
-  Entry* entry = service_->entries().front().get();
-  ASSERT_EQ(sessions::TabRestoreService::TAB, entry->type);
-  Tab* tab = static_cast<Tab*>(entry);
-  ASSERT_EQ(2U, tab->navigations.size());
-  EXPECT_EQ(url1_, tab->navigations[0].virtual_url());
-  EXPECT_EQ(0, tab->navigations[0].index());
-  EXPECT_EQ(url3_, tab->navigations[1].virtual_url());
-  EXPECT_EQ(1, tab->navigations[1].index());
-  EXPECT_EQ(1, tab->current_navigation_index);
-
-  // Delete all entries.
-  service_->DeleteNavigationEntries(base::BindRepeating(
-      [](const SerializedNavigationEntry& entry) { return true; }));
-  // Recreate the service and have it load the tabs.
-  RecreateService();
-  // The entry should be removed.
-  ASSERT_EQ(0U, service_->entries().size());
-}
-
 // Make sure we persist entries to disk that have post data.
 TEST_F(PersistentTabRestoreServiceTest, DontPersistPostData) {
   AddThreeNavigations();
@@ -443,7 +359,8 @@
   // One entry should be created.
   ASSERT_EQ(1U, service_->entries().size());
 
-  const Entry* restored_entry = service_->entries().front().get();
+  const sessions::TabRestoreService::Entry* restored_entry =
+      service_->entries().front().get();
   ASSERT_EQ(sessions::TabRestoreService::TAB, restored_entry->type);
 
   const Tab* restored_tab =
@@ -490,7 +407,8 @@
 
   // Make sure we get back one entry with one tab whose url is url1.
   ASSERT_EQ(1U, service_->entries().size());
-  Entry* entry2 = service_->entries().front().get();
+  sessions::TabRestoreService::Entry* entry2 =
+      service_->entries().front().get();
   ASSERT_EQ(sessions::TabRestoreService::WINDOW, entry2->type);
   sessions::TabRestoreService::Window* window =
       static_cast<sessions::TabRestoreService::Window*>(entry2);
@@ -549,7 +467,7 @@
   // the tab restore service. The previous session entry should be first.
   ASSERT_EQ(2U, service_->entries().size());
   // The first entry should come from the session service.
-  Entry* entry = service_->entries().front().get();
+  sessions::TabRestoreService::Entry* entry = service_->entries().front().get();
   ASSERT_EQ(sessions::TabRestoreService::WINDOW, entry->type);
   sessions::TabRestoreService::Window* window =
       static_cast<sessions::TabRestoreService::Window*>(entry);
@@ -603,7 +521,7 @@
   ASSERT_EQ(2U, service_->entries().size());
 
   // The first entry should come from the session service.
-  Entry* entry = service_->entries().front().get();
+  sessions::TabRestoreService::Entry* entry = service_->entries().front().get();
   ASSERT_EQ(sessions::TabRestoreService::WINDOW, entry->type);
   sessions::TabRestoreService::Window* window =
       static_cast<sessions::TabRestoreService::Window*>(entry);
@@ -646,7 +564,7 @@
   // the tab restore service. The previous session entry should be first.
   ASSERT_EQ(2U, service_->entries().size());
   // The first entry should come from the session service.
-  Entry* entry = service_->entries().front().get();
+  sessions::TabRestoreService::Entry* entry = service_->entries().front().get();
   ASSERT_EQ(sessions::TabRestoreService::WINDOW, entry->type);
   sessions::TabRestoreService::Window* window =
       static_cast<sessions::TabRestoreService::Window*>(entry);
@@ -691,7 +609,7 @@
   ASSERT_EQ(static_cast<size_t>(kMaxEntries), service_->entries().size());
 
   // The first entry should come from the session service.
-  Entry* entry = service_->entries().front().get();
+  sessions::TabRestoreService::Entry* entry = service_->entries().front().get();
   ASSERT_EQ(sessions::TabRestoreService::WINDOW, entry->type);
   sessions::TabRestoreService::Window* window =
       static_cast<sessions::TabRestoreService::Window*>(entry);
@@ -720,7 +638,8 @@
   std::vector<SerializedNavigationEntry> old_navigations;
   {
     // |entry|/|tab| doesn't survive after RecreateService().
-    Entry* entry = service_->entries().front().get();
+    sessions::TabRestoreService::Entry* entry =
+        service_->entries().front().get();
     ASSERT_EQ(sessions::TabRestoreService::TAB, entry->type);
     Tab* tab = static_cast<Tab*>(entry);
     tab->timestamp = tab_timestamp;
@@ -741,7 +660,8 @@
   ASSERT_EQ(1U, service_->entries().size());
 
   // And verify the entry.
-  Entry* restored_entry = service_->entries().front().get();
+  sessions::TabRestoreService::Entry* restored_entry =
+      service_->entries().front().get();
   ASSERT_EQ(sessions::TabRestoreService::TAB, restored_entry->type);
   Tab* restored_tab =
       static_cast<Tab*>(restored_entry);
@@ -768,7 +688,8 @@
   std::vector<sessions::SerializedNavigationEntry> old_navigations;
   {
     // |entry|/|tab| doesn't survive after RecreateService().
-    Entry* entry = service_->entries().front().get();
+    sessions::TabRestoreService::Entry* entry =
+        service_->entries().front().get();
     ASSERT_EQ(sessions::TabRestoreService::TAB, entry->type);
     Tab* tab = static_cast<Tab*>(entry);
     old_navigations = tab->navigations;
@@ -788,7 +709,8 @@
   ASSERT_EQ(1U, service_->entries().size());
 
   // And verify the entry.
-  Entry* restored_entry = service_->entries().front().get();
+  sessions::TabRestoreService::Entry* restored_entry =
+      service_->entries().front().get();
   ASSERT_EQ(sessions::TabRestoreService::TAB, restored_entry->type);
   Tab* restored_tab =
       static_cast<Tab*>(restored_entry);
diff --git a/chrome/browser/sessions/session_restore_browsertest.cc b/chrome/browser/sessions/session_restore_browsertest.cc
index 5f67ee4..082b2f2 100644
--- a/chrome/browser/sessions/session_restore_browsertest.cc
+++ b/chrome/browser/sessions/session_restore_browsertest.cc
@@ -16,7 +16,6 @@
 #include "base/memory/ptr_util.h"
 #include "base/process/launch.h"
 #include "base/strings/utf_string_conversions.h"
-#include "base/test/bind_test_util.h"
 #include "base/threading/thread_restrictions.h"
 #include "base/time/time.h"
 #include "build/build_config.h"
@@ -856,33 +855,6 @@
             new_browser->tab_strip_model()->GetActiveWebContents()->GetURL());
 }
 
-IN_PROC_BROWSER_TEST_F(SessionRestoreTest, RestoreAfterDelete) {
-  ui_test_utils::NavigateToURL(browser(), url1_);
-  ui_test_utils::NavigateToURL(browser(), url2_);
-  ui_test_utils::NavigateToURL(browser(), url3_);
-
-  content::NavigationController& controller =
-      browser()->tab_strip_model()->GetActiveWebContents()->GetController();
-  // Three urls and the NTP.
-  EXPECT_EQ(4, controller.GetEntryCount());
-  controller.DeleteNavigationEntries(
-      base::BindLambdaForTesting([&](const content::NavigationEntry& entry) {
-        return entry.GetURL() == url2_;
-      }));
-  EXPECT_EQ(3, controller.GetEntryCount());
-
-  Browser* new_browser = QuitBrowserAndRestore(browser(), 1);
-  content::NavigationController& new_controller =
-      new_browser->tab_strip_model()->GetActiveWebContents()->GetController();
-  EXPECT_EQ(3, new_controller.GetEntryCount());
-  ASSERT_EQ(1u, active_browser_list_->size());
-  ASSERT_EQ(url3_,
-            new_browser->tab_strip_model()->GetActiveWebContents()->GetURL());
-  GoBack(new_browser);
-  ASSERT_EQ(url1_,
-            new_browser->tab_strip_model()->GetActiveWebContents()->GetURL());
-}
-
 IN_PROC_BROWSER_TEST_F(SessionRestoreTest, StartupPagesWithOnlyNtp) {
   ui_test_utils::NavigateToURL(browser(), GURL(chrome::kChromeUINewTabURL));
   SessionStartupPref pref(SessionStartupPref::URLS);
diff --git a/chrome/browser/sessions/session_service.cc b/chrome/browser/sessions/session_service.cc
index bda4047..3b09cc30 100644
--- a/chrome/browser/sessions/session_service.cc
+++ b/chrome/browser/sessions/session_service.cc
@@ -75,7 +75,6 @@
       has_open_trackable_browsers_(false),
       move_on_new_browser_(false),
       force_browser_not_alive_with_no_windows_(false),
-      rebuild_on_next_save_(false),
       weak_factory_(this) {
   // We should never be created when incognito.
   DCHECK(!profile->IsOffTheRecord());
@@ -243,11 +242,6 @@
   if (!ShouldTrackChangesToWindow(window_id))
     return;
 
-  // If Chrome is closed immediately after a history deletion, we have to
-  // rebuild commands before this window is closed, otherwise these tabs would
-  // be lost.
-  RebuildCommandsIfRequired();
-
   // The window is about to close. If there are other tabbed browsers with the
   // same original profile commit the close immediately.
   //
@@ -410,17 +404,6 @@
       sessions::CreateTabNavigationPathPrunedFromFrontCommand(tab_id, count));
 }
 
-void SessionService::TabNavigationPathEntriesDeleted(const SessionID& window_id,
-                                                     const SessionID& tab_id) {
-  if (!ShouldTrackChangesToWindow(window_id))
-    return;
-
-  // Multiple tabs might be affected by this deletion, so the rebuild is
-  // delayed until next save.
-  rebuild_on_next_save_ = true;
-  base_session_service_->StartSaveTimer();
-}
-
 void SessionService::UpdateTabNavigation(
     const SessionID& window_id,
     const SessionID& tab_id,
@@ -525,16 +508,6 @@
   return should_use_delayed_save_;
 }
 
-void SessionService::OnWillSaveCommands() {
-  RebuildCommandsIfRequired();
-}
-
-void SessionService::RebuildCommandsIfRequired() {
-  if (rebuild_on_next_save_ && pending_window_close_ids_.empty()) {
-    ScheduleResetCommands();
-  }
-}
-
 void SessionService::Init() {
   BrowserList::AddObserver(this);
 }
@@ -759,7 +732,6 @@
   base_session_service_->ClearPendingCommands();
   tab_to_available_range_.clear();
   windows_tracking_.clear();
-  rebuild_on_next_save_ = false;
   BuildCommandsFromBrowsers(&tab_to_available_range_,
                             &windows_tracking_);
   if (!windows_tracking_.empty()) {
diff --git a/chrome/browser/sessions/session_service.h b/chrome/browser/sessions/session_service.h
index 4961a2b..c78ccb31 100644
--- a/chrome/browser/sessions/session_service.h
+++ b/chrome/browser/sessions/session_service.h
@@ -174,11 +174,6 @@
                                         const SessionID& tab_id,
                                         int count);
 
-  // Invoked when the NavigationController has deleted entries because of a
-  // history deletion.
-  void TabNavigationPathEntriesDeleted(const SessionID& window_id,
-                                       const SessionID& tab_id);
-
   // Updates the navigation entry for the specified tab.
   void UpdateTabNavigation(
       const SessionID& window_id,
@@ -222,7 +217,6 @@
 
   // BaseSessionServiceDelegate:
   bool ShouldUseDelayedSave() override;
-  void OnWillSaveCommands() override;
 
  private:
   // Allow tests to access our innards for testing purposes.
@@ -314,9 +308,6 @@
   // Returns true if we track changes to the specified browser.
   bool ShouldTrackBrowser(Browser* browser) const;
 
-  // Will rebuild session commands if rebuild_on_next_save_ is true.
-  void RebuildCommandsIfRequired();
-
   // Call when certain session relevant notifications
   // (tab_closed, nav_list_pruned) occur.  In addition, this is
   // currently called when Save() is called to compare how often the
@@ -383,9 +374,6 @@
   // without quitting.
   bool force_browser_not_alive_with_no_windows_;
 
-  // Force session commands to be rebuild before next save event.
-  bool rebuild_on_next_save_;
-
   base::WeakPtrFactory<SessionService> weak_factory_;
 
   DISALLOW_COPY_AND_ASSIGN(SessionService);
diff --git a/chrome/browser/sessions/session_tab_helper.cc b/chrome/browser/sessions/session_tab_helper.cc
index a5d23d8..5595366 100644
--- a/chrome/browser/sessions/session_tab_helper.cc
+++ b/chrome/browser/sessions/session_tab_helper.cc
@@ -94,15 +94,6 @@
   }
 }
 
-void SessionTabHelper::NavigationEntriesDeleted() {
-  SessionService* session_service = SessionServiceFactory::GetForProfile(
-      Profile::FromBrowserContext(web_contents()->GetBrowserContext()));
-  if (!session_service)
-    return;
-
-  session_service->TabNavigationPathEntriesDeleted(window_id(), session_id());
-}
-
 void SessionTabHelper::NavigationEntryChanged(
     const content::EntryChangedDetails& change_details) {
   SessionService* session_service = SessionServiceFactory::GetForProfile(
diff --git a/chrome/browser/sessions/session_tab_helper.h b/chrome/browser/sessions/session_tab_helper.h
index 98d0692..cf73a0c 100644
--- a/chrome/browser/sessions/session_tab_helper.h
+++ b/chrome/browser/sessions/session_tab_helper.h
@@ -51,7 +51,6 @@
       const content::LoadCommittedDetails& load_details) override;
   void NavigationListPruned(
       const content::PrunedDetails& pruned_details) override;
-  void NavigationEntriesDeleted() override;
   void NavigationEntryChanged(
       const content::EntryChangedDetails& change_details) override;
 #endif
diff --git a/chrome/browser/sessions/tab_restore_browsertest.cc b/chrome/browser/sessions/tab_restore_browsertest.cc
index 9bdc4f0..4610e274 100644
--- a/chrome/browser/sessions/tab_restore_browsertest.cc
+++ b/chrome/browser/sessions/tab_restore_browsertest.cc
@@ -71,6 +71,8 @@
 
  private:
   // Overridden from TabRestoreServiceObserver:
+  void TabRestoreServiceChanged(sessions::TabRestoreService* service) override {
+  }
   void TabRestoreServiceDestroyed(
       sessions::TabRestoreService* service) override {}
   void TabRestoreServiceLoaded(sessions::TabRestoreService* service) override {
diff --git a/chrome/browser/signin/signin_promo.cc b/chrome/browser/signin/signin_promo.cc
index 7b933749..4c7f522 100644
--- a/chrome/browser/signin/signin_promo.cc
+++ b/chrome/browser/signin/signin_promo.cc
@@ -386,6 +386,7 @@
   registry->RegisterBooleanPref(prefs::kSignInPromoUserSkipped, false);
   registry->RegisterBooleanPref(prefs::kSignInPromoShowOnFirstRunAllowed, true);
   registry->RegisterBooleanPref(prefs::kSignInPromoShowNTPBubble, false);
+  registry->RegisterIntegerPref(prefs::kDiceSigninUserMenuPromoCount, 0);
 }
 
 }  // namespace signin
diff --git a/chrome/browser/sync/test/integration/profile_sync_service_harness.cc b/chrome/browser/sync/test/integration/profile_sync_service_harness.cc
index 22b45bd..1a2fd16 100644
--- a/chrome/browser/sync/test/integration/profile_sync_service_harness.cc
+++ b/chrome/browser/sync/test/integration/profile_sync_service_harness.cc
@@ -14,7 +14,7 @@
 #include "base/memory/ptr_util.h"
 #include "base/strings/stringprintf.h"
 #include "chrome/browser/profiles/profile.h"
-#include "chrome/browser/signin/profile_oauth2_token_service_factory.h"
+#include "chrome/browser/signin/identity_manager_factory.h"
 #include "chrome/browser/sync/profile_sync_service_factory.h"
 #include "chrome/browser/sync/test/integration/quiesce_status_change_checker.h"
 #include "chrome/browser/sync/test/integration/single_client_status_change_checker.h"
@@ -27,12 +27,11 @@
 #include "chrome/common/chrome_switches.h"
 #include "components/browser_sync/profile_sync_service.h"
 #include "components/invalidation/impl/p2p_invalidation_service.h"
-#include "components/signin/core/browser/profile_oauth2_token_service.h"
-#include "components/signin/core/browser/signin_manager_base.h"
 #include "components/sync/base/progress_marker_map.h"
 #include "components/sync/driver/about_sync_util.h"
 #include "components/sync/engine/sync_string_conversions.h"
 #include "google_apis/gaia/gaia_constants.h"
+#include "services/identity/public/cpp/identity_manager.h"
 
 using browser_sync::ProfileSyncService;
 using syncer::SyncCycleSnapshot;
@@ -170,11 +169,16 @@
     }
   } else if (signin_type_ == SigninType::FAKE_SIGNIN) {
     // Authenticate sync client using GAIA credentials.
-    service()->signin()->SetAuthenticatedAccountInfo(gaia_id_, username_);
-    std::string account_id = service()->signin()->GetAuthenticatedAccountId();
+    // TODO(https://crbug.com/814307): This ideally should go through
+    // identity_test_utils.h (and in the long run IdentityTestEnvironment), but
+    // making that change is complex for reasons described in the bug.
+    identity::IdentityManager* identity_manager =
+        IdentityManagerFactory::GetForProfile(profile_);
+    identity_manager->SetPrimaryAccountSynchronouslyForTests(
+        gaia_id_, username_, GenerateFakeOAuth2RefreshTokenString());
+    std::string account_id =
+        identity_manager->GetPrimaryAccountInfo().account_id;
     service()->GoogleSigninSucceeded(account_id, username_);
-    ProfileOAuth2TokenServiceFactory::GetForProfile(profile_)->
-      UpdateCredentials(account_id, GenerateFakeOAuth2RefreshTokenString());
   } else {
     LOG(ERROR) << "Unsupported profile signin type.";
   }
diff --git a/chrome/browser/sync/test/integration/two_client_typed_urls_sync_test.cc b/chrome/browser/sync/test/integration/two_client_typed_urls_sync_test.cc
index 1bc9f6c..8e24e2f 100644
--- a/chrome/browser/sync/test/integration/two_client_typed_urls_sync_test.cc
+++ b/chrome/browser/sync/test/integration/two_client_typed_urls_sync_test.cc
@@ -241,8 +241,7 @@
   ASSERT_TRUE(ProfilesHaveSameURLsChecker().Wait());
 }
 
-// flaky, see crbug.com/108511
-IN_PROC_BROWSER_TEST_F(TwoClientTypedUrlsSyncTest, DISABLED_AddOneDeleteOther) {
+IN_PROC_BROWSER_TEST_F(TwoClientTypedUrlsSyncTest, AddOneDeleteOther) {
   const base::string16 kHistoryUrl(
       ASCIIToUTF16("http://www.add-one-delete-history.google.com/"));
   ASSERT_TRUE(SetupSync()) << "SetupSync() failed.";
@@ -266,9 +265,7 @@
   ASSERT_TRUE(ProfilesHaveSameURLsChecker().Wait());
 }
 
-// flaky, see crbug.com/108511
-IN_PROC_BROWSER_TEST_F(TwoClientTypedUrlsSyncTest,
-                       DISABLED_AddOneDeleteOtherAddAgain) {
+IN_PROC_BROWSER_TEST_F(TwoClientTypedUrlsSyncTest, AddOneDeleteOtherAddAgain) {
   const base::string16 kHistoryUrl(
       ASCIIToUTF16("http://www.add-delete-add-history.google.com/"));
   ASSERT_TRUE(SetupSync()) << "SetupSync() failed.";
diff --git a/chrome/browser/ui/BUILD.gn b/chrome/browser/ui/BUILD.gn
index 2170e90c..4bbc598 100644
--- a/chrome/browser/ui/BUILD.gn
+++ b/chrome/browser/ui/BUILD.gn
@@ -1433,8 +1433,10 @@
       "scoped_tabbed_browser_displayer.h",
       "search/instant_controller.cc",
       "search/instant_controller.h",
-      "search/new_tab_page_navigation_throttle.cc",
-      "search/new_tab_page_navigation_throttle.h",
+      "search/new_tab_page_interceptor_service.cc",
+      "search/new_tab_page_interceptor_service.h",
+      "search/new_tab_page_interceptor_service_factory.cc",
+      "search/new_tab_page_interceptor_service_factory.h",
       "search/ntp_user_data_logger.cc",
       "search/ntp_user_data_logger.h",
       "search/search_ipc_router.cc",
@@ -2796,6 +2798,8 @@
       "views/extensions/pwa_confirmation_view.h",
       "views/extensions/web_app_info_image_source.cc",
       "views/extensions/web_app_info_image_source.h",
+      "views/folder_upload_confirmation_view.cc",
+      "views/folder_upload_confirmation_view.h",
       "views/fullscreen_control/fullscreen_control_host.cc",
       "views/fullscreen_control/fullscreen_control_host.h",
       "views/fullscreen_control/fullscreen_control_popup.cc",
diff --git a/chrome/browser/ui/ash/wallpaper_controller_client.cc b/chrome/browser/ui/ash/wallpaper_controller_client.cc
index 42779cb..28edbda 100644
--- a/chrome/browser/ui/ash/wallpaper_controller_client.cc
+++ b/chrome/browser/ui/ash/wallpaper_controller_client.cc
@@ -9,6 +9,7 @@
 #include "base/path_service.h"
 #include "base/sha1.h"
 #include "base/strings/string_number_conversions.h"
+#include "chrome/browser/chrome_notification_types.h"
 #include "chrome/browser/chromeos/customization/customization_wallpaper_util.h"
 #include "chrome/browser/chromeos/login/startup_utils.h"
 #include "chrome/browser/chromeos/login/wizard_controller.h"
@@ -24,6 +25,7 @@
 #include "components/user_manager/known_user.h"
 #include "components/user_manager/user_manager.h"
 #include "components/wallpaper/wallpaper_files_id.h"
+#include "content/public/browser/notification_service.h"
 #include "content/public/common/service_manager_connection.h"
 #include "extensions/browser/extension_system.h"
 #include "extensions/common/constants.h"
@@ -475,3 +477,10 @@
 
   UpdateRegisteredDeviceWallpaper();
 }
+
+void WallpaperControllerClient::OnFirstWallpaperAnimationFinished() {
+  content::NotificationService::current()->Notify(
+      chrome::NOTIFICATION_WALLPAPER_ANIMATION_FINISHED,
+      content::NotificationService::AllSources(),
+      content::NotificationService::NoDetails());
+}
diff --git a/chrome/browser/ui/ash/wallpaper_controller_client.h b/chrome/browser/ui/ash/wallpaper_controller_client.h
index 6445f6c..e05c9015 100644
--- a/chrome/browser/ui/ash/wallpaper_controller_client.h
+++ b/chrome/browser/ui/ash/wallpaper_controller_client.h
@@ -87,6 +87,7 @@
   // ash::mojom::WallpaperControllerClient:
   void OpenWallpaperPicker() override;
   void OnReadyToSetWallpaper() override;
+  void OnFirstWallpaperAnimationFinished() override;
 
   // WallpaperController interface in ash.
   ash::mojom::WallpaperControllerPtr wallpaper_controller_;
diff --git a/chrome/browser/ui/browser_browsertest.cc b/chrome/browser/ui/browser_browsertest.cc
index 6013607..c433f0a 100644
--- a/chrome/browser/ui/browser_browsertest.cc
+++ b/chrome/browser/ui/browser_browsertest.cc
@@ -57,7 +57,6 @@
 #include "chrome/browser/ui/extensions/app_launch_params.h"
 #include "chrome/browser/ui/extensions/application_launch.h"
 #include "chrome/browser/ui/javascript_dialogs/javascript_dialog_tab_helper.h"
-#include "chrome/browser/ui/search/local_ntp_test_utils.h"
 #include "chrome/browser/ui/search/search_tab_helper.h"
 #include "chrome/browser/ui/startup/startup_browser_creator.h"
 #include "chrome/browser/ui/startup/startup_browser_creator_impl.h"
@@ -518,7 +517,8 @@
   ASSERT_TRUE(embedded_test_server()->Start());
   WebContents* web_contents =
       browser()->tab_strip_model()->GetActiveWebContents();
-  ui_test_utils::NavigateToURL(browser(), GURL(chrome::kChromeUINewTabURL));
+  GURL ntp_url(search::GetNewTabPageURL(browser()->profile()));
+  ui_test_utils::NavigateToURL(browser(), ntp_url);
 
   // Navigate to a 204 URL (aborts with no content) on the NTP and make sure it
   // sticks around so that the user can edit it.
@@ -1143,7 +1143,9 @@
 
   // There should only be one tab now, with the NTP loaded.
   ASSERT_EQ(1, tab_strip_model->count());
-  EXPECT_TRUE(search::IsInstantNTP(tab_strip_model->GetActiveWebContents()));
+  EXPECT_EQ(
+      chrome::kChromeUINewTabURL,
+      tab_strip_model->GetActiveWebContents()->GetLastCommittedURL().spec());
 }
 
 // Open with --app-id=<id>, and see that an application tab opens by default.
@@ -1235,7 +1237,8 @@
   ASSERT_TRUE(embedded_test_server()->Start());
   WebContents* web_contents =
       browser()->tab_strip_model()->GetActiveWebContents();
-  ui_test_utils::NavigateToURL(browser(), GURL(chrome::kChromeUINewTabURL));
+  GURL ntp_url = search::GetNewTabPageURL(browser()->profile());
+  ui_test_utils::NavigateToURL(browser(), ntp_url);
 
   // Open a devtools window.
   DevToolsWindow* devtools_window =
diff --git a/chrome/browser/ui/browser_dialogs.cc b/chrome/browser/ui/browser_dialogs.cc
index f8f1782..847401d 100644
--- a/chrome/browser/ui/browser_dialogs.cc
+++ b/chrome/browser/ui/browser_dialogs.cc
@@ -14,3 +14,15 @@
 }
 
 }  // namespace chrome
+
+#if !defined(TOOLKIT_VIEWS)
+// There's no dialog version of this available outside views, run callback as if
+// the dialog was instantly accepted.
+void ShowFolderUploadConfirmationDialog(
+    const base::FilePath& path,
+    base::OnceCallback<void(const std::vector<ui::SelectedFileInfo>&)> callback,
+    std::vector<ui::SelectedFileInfo> selected_files,
+    content::WebContents* web_contents) {
+  std::move(callback).Run(selected_files);
+}
+#endif  // !defined(TOOLKIT_VIEWS)
diff --git a/chrome/browser/ui/browser_dialogs.h b/chrome/browser/ui/browser_dialogs.h
index ebaec1ce..f5fb0c1 100644
--- a/chrome/browser/ui/browser_dialogs.h
+++ b/chrome/browser/ui/browser_dialogs.h
@@ -28,6 +28,10 @@
 class WebShareTarget;
 struct WebApplicationInfo;
 
+namespace base {
+class FilePath;
+}
+
 namespace content {
 class BrowserContext;
 class ColorChooser;
@@ -61,6 +65,7 @@
 
 namespace ui {
 class WebDialogDelegate;
+struct SelectedFileInfo;
 }
 
 namespace views {
@@ -324,4 +329,10 @@
 
 #endif  // OS_CHROMEOS
 
+void ShowFolderUploadConfirmationDialog(
+    const base::FilePath& path,
+    base::OnceCallback<void(const std::vector<ui::SelectedFileInfo>&)> callback,
+    std::vector<ui::SelectedFileInfo> selected_files,
+    content::WebContents* web_contents);
+
 #endif  // CHROME_BROWSER_UI_BROWSER_DIALOGS_H_
diff --git a/chrome/browser/ui/browser_navigator_browsertest.cc b/chrome/browser/ui/browser_navigator_browsertest.cc
index b7f96211..7056256b 100644
--- a/chrome/browser/ui/browser_navigator_browsertest.cc
+++ b/chrome/browser/ui/browser_navigator_browsertest.cc
@@ -12,7 +12,6 @@
 #include "chrome/browser/prefs/incognito_mode_prefs.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/renderer_host/chrome_navigation_ui_data.h"
-#include "chrome/browser/search/search.h"
 #include "chrome/browser/ui/browser.h"
 #include "chrome/browser/ui/browser_commands.h"
 #include "chrome/browser/ui/browser_finder.h"
@@ -22,7 +21,6 @@
 #include "chrome/browser/ui/browser_window.h"
 #include "chrome/browser/ui/chrome_pages.h"
 #include "chrome/browser/ui/location_bar/location_bar.h"
-#include "chrome/browser/ui/search/local_ntp_test_utils.h"
 #include "chrome/browser/ui/singleton_tabs.h"
 #include "chrome/browser/ui/tabs/tab_strip_model.h"
 #include "chrome/common/pref_names.h"
@@ -1351,8 +1349,8 @@
   params.url = GURL(chrome::kChromeUINewTabURL);
   ui_test_utils::NavigateToURL(&params);
   EXPECT_EQ(1, browser()->tab_strip_model()->count());
-  EXPECT_TRUE(search::IsInstantNTP(
-      browser()->tab_strip_model()->GetActiveWebContents()));
+  EXPECT_EQ(GURL(chrome::kChromeUINewTabURL),
+            browser()->tab_strip_model()->GetActiveWebContents()->GetURL());
 
   {
     content::WindowedNotificationObserver observer(
diff --git a/chrome/browser/ui/browser_tab_restorer.cc b/chrome/browser/ui/browser_tab_restorer.cc
index dcee143..4bbabc4 100644
--- a/chrome/browser/ui/browser_tab_restorer.cc
+++ b/chrome/browser/ui/browser_tab_restorer.cc
@@ -40,6 +40,7 @@
   explicit BrowserTabRestorer(Browser* browser);
 
   // TabRestoreServiceObserver:
+  void TabRestoreServiceChanged(sessions::TabRestoreService* service) override;
   void TabRestoreServiceDestroyed(
       sessions::TabRestoreService* service) override;
   void TabRestoreServiceLoaded(sessions::TabRestoreService* service) override;
@@ -81,6 +82,9 @@
   tab_restore_service_->LoadTabsFromLastSession();
 }
 
+void BrowserTabRestorer::TabRestoreServiceChanged(
+    sessions::TabRestoreService* service) {}
+
 void BrowserTabRestorer::TabRestoreServiceDestroyed(
     sessions::TabRestoreService* service) {}
 
diff --git a/chrome/browser/ui/input_method/input_method_engine.cc b/chrome/browser/ui/input_method/input_method_engine.cc
index d6ac803..7ad0f90 100644
--- a/chrome/browser/ui/input_method/input_method_engine.cc
+++ b/chrome/browser/ui/input_method/input_method_engine.cc
@@ -255,8 +255,7 @@
   // Checks if the last committed url is whitelisted url.
   url::Origin origin = url::Origin::Create(url);
   std::vector<GURL> whitelist_urls{GURL(url::kAboutBlankURL),
-                                   GURL(chrome::kChromeUINewTabURL),
-                                   GURL(chrome::kChromeSearchLocalNtpUrl)};
+                                   GURL(chrome::kChromeUINewTabURL)};
   for (const GURL& whitelist_url : whitelist_urls) {
     if (url::Origin::Create(whitelist_url).IsSameOriginWith(origin))
       return false;
diff --git a/chrome/browser/ui/javascript_dialogs/javascript_dialog_browsertest.cc b/chrome/browser/ui/javascript_dialogs/javascript_dialog_browsertest.cc
index 400975b..20b7743 100644
--- a/chrome/browser/ui/javascript_dialogs/javascript_dialog_browsertest.cc
+++ b/chrome/browser/ui/javascript_dialogs/javascript_dialog_browsertest.cc
@@ -126,6 +126,7 @@
 IN_PROC_BROWSER_TEST_F(JavaScriptDialogTest, HandleJavaScriptDialog) {
   content::WebContents* tab =
       browser()->tab_strip_model()->GetActiveWebContents();
+  content::RenderFrameHost* frame = tab->GetMainFrame();
   JavaScriptDialogTabHelper* js_helper =
       JavaScriptDialogTabHelper::FromWebContents(tab);
 
@@ -134,7 +135,7 @@
   // alert
   bool did_suppress = false;
   js_helper->RunJavaScriptDialog(
-      tab, GURL(), content::JAVASCRIPT_DIALOG_TYPE_ALERT, base::string16(),
+      tab, frame, content::JAVASCRIPT_DIALOG_TYPE_ALERT, base::string16(),
       base::string16(), callback_helper.GetCallback(), &did_suppress);
   ASSERT_TRUE(js_helper->IsShowingDialogForTesting());
   js_helper->HandleJavaScriptDialog(tab, true, nullptr);
@@ -145,7 +146,7 @@
   // confirm
   for (auto response : {true, false}) {
     js_helper->RunJavaScriptDialog(
-        tab, GURL(), content::JAVASCRIPT_DIALOG_TYPE_CONFIRM, base::string16(),
+        tab, frame, content::JAVASCRIPT_DIALOG_TYPE_CONFIRM, base::string16(),
         base::string16(), callback_helper.GetCallback(), &did_suppress);
     ASSERT_TRUE(js_helper->IsShowingDialogForTesting());
     js_helper->HandleJavaScriptDialog(tab, response, nullptr);
@@ -155,7 +156,7 @@
   }
 
   // prompt, cancel
-  js_helper->RunJavaScriptDialog(tab, GURL(),
+  js_helper->RunJavaScriptDialog(tab, frame,
                                  content::JAVASCRIPT_DIALOG_TYPE_PROMPT,
                                  base::ASCIIToUTF16("Label"), base::string16(),
                                  callback_helper.GetCallback(), &did_suppress);
@@ -169,7 +170,7 @@
   base::string16 value2 = base::ASCIIToUTF16("123");
 
   // prompt, ok + override
-  js_helper->RunJavaScriptDialog(tab, GURL(),
+  js_helper->RunJavaScriptDialog(tab, frame,
                                  content::JAVASCRIPT_DIALOG_TYPE_PROMPT,
                                  base::ASCIIToUTF16("Label"), value1,
                                  callback_helper.GetCallback(), &did_suppress);
@@ -180,7 +181,7 @@
   ASSERT_EQ(value2, callback_helper.last_input());
 
   // prompt, ok + no override
-  js_helper->RunJavaScriptDialog(tab, GURL(),
+  js_helper->RunJavaScriptDialog(tab, frame,
                                  content::JAVASCRIPT_DIALOG_TYPE_PROMPT,
                                  base::ASCIIToUTF16("Label"), value1,
                                  callback_helper.GetCallback(), &did_suppress);
diff --git a/chrome/browser/ui/javascript_dialogs/javascript_dialog_tab_helper.cc b/chrome/browser/ui/javascript_dialogs/javascript_dialog_tab_helper.cc
index 647cf16..6807250 100644
--- a/chrome/browser/ui/javascript_dialogs/javascript_dialog_tab_helper.cc
+++ b/chrome/browser/ui/javascript_dialogs/javascript_dialog_tab_helper.cc
@@ -80,12 +80,14 @@
 
 void JavaScriptDialogTabHelper::RunJavaScriptDialog(
     content::WebContents* alerting_web_contents,
-    const GURL& alerting_frame_url,
+    content::RenderFrameHost* render_frame_host,
     content::JavaScriptDialogType dialog_type,
     const base::string16& message_text,
     const base::string16& default_prompt_text,
     DialogClosedCallback callback,
     bool* did_suppress_message) {
+  GURL alerting_frame_url = render_frame_host->GetLastCommittedURL();
+
   content::WebContents* parent_web_contents =
       WebContentsObserver::web_contents();
   bool foremost = IsWebContentsForemost(parent_web_contents);
diff --git a/chrome/browser/ui/javascript_dialogs/javascript_dialog_tab_helper.h b/chrome/browser/ui/javascript_dialogs/javascript_dialog_tab_helper.h
index e80d402..00c4c26 100644
--- a/chrome/browser/ui/javascript_dialogs/javascript_dialog_tab_helper.h
+++ b/chrome/browser/ui/javascript_dialogs/javascript_dialog_tab_helper.h
@@ -56,7 +56,7 @@
 
   // JavaScriptDialogManager:
   void RunJavaScriptDialog(content::WebContents* web_contents,
-                           const GURL& alerting_frame_url,
+                           content::RenderFrameHost* render_frame_host,
                            content::JavaScriptDialogType dialog_type,
                            const base::string16& message_text,
                            const base::string16& default_prompt_text,
diff --git a/chrome/browser/ui/screen_capture_notification_ui_browsertest.cc b/chrome/browser/ui/screen_capture_notification_ui_browsertest.cc
new file mode 100644
index 0000000..97bb0271
--- /dev/null
+++ b/chrome/browser/ui/screen_capture_notification_ui_browsertest.cc
@@ -0,0 +1,68 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/ui/screen_capture_notification_ui.h"
+
+#include <memory>
+
+#include "base/bind.h"
+#include "base/macros.h"
+#include "base/run_loop.h"
+#include "base/strings/string16.h"
+#include "base/strings/utf_string_conversions.h"
+#include "chrome/browser/ui/test/test_browser_dialog.h"
+#include "content/public/test/browser_test.h"
+#include "ui/gfx/native_widget_types.h"
+
+class ScreenCaptureNotificationUiBrowserTest : public DialogBrowserTest {
+ public:
+  ScreenCaptureNotificationUiBrowserTest() = default;
+  ~ScreenCaptureNotificationUiBrowserTest() override = default;
+
+  // TestBrowserUi:
+  void ShowUi(const std::string& name) override {
+    screen_capture_notification_ui_ =
+        ScreenCaptureNotificationUI::Create(base::string16(
+            base::ASCIIToUTF16("ScreenCaptureNotificationUI Browser Test")));
+    on_started_result_ =
+        screen_capture_notification_ui_->OnStarted(base::BindRepeating(
+            [](ScreenCaptureNotificationUiBrowserTest* test) {
+              if (test->run_loop_)
+                test->run_loop_->QuitWhenIdle();
+            },
+            base::Unretained(this)));
+  }
+
+  bool VerifyUi() override {
+    // on_started_result_ == 0 is a loose signal that indicates
+    // DialogBrowserTest::VerifyUi() should be called instead of checking the
+    // value of |on_started_result_|.
+    // on_started_result_ will occur under the following circumstances:
+    //   * Views ScreenCaptureNotificationUI except for Windows.
+    //   * ChromeOS (Currently unsupported and not built for this test as the
+    //     CrOS system tray is used).
+    // TODO(robliao): Remove this override once Views is the only toolkit.
+    return (on_started_result_ != 0) || DialogBrowserTest::VerifyUi();
+  }
+
+  void DismissUi() override { screen_capture_notification_ui_.reset(); }
+
+  void WaitForUserDismissal() override {
+    run_loop_ = std::make_unique<base::RunLoop>();
+    run_loop_->Run();
+    run_loop_.reset();
+    screen_capture_notification_ui_.reset();
+  }
+
+ private:
+  std::unique_ptr<ScreenCaptureNotificationUI> screen_capture_notification_ui_;
+  gfx::NativeViewId on_started_result_;
+  std::unique_ptr<base::RunLoop> run_loop_;
+
+  DISALLOW_COPY_AND_ASSIGN(ScreenCaptureNotificationUiBrowserTest);
+};
+
+IN_PROC_BROWSER_TEST_F(ScreenCaptureNotificationUiBrowserTest, InvokeUi) {
+  ShowAndVerifyUi();
+}
diff --git a/chrome/browser/ui/search/local_ntp_test_utils.cc b/chrome/browser/ui/search/local_ntp_test_utils.cc
index 23b82682..7288628 100644
--- a/chrome/browser/ui/search/local_ntp_test_utils.cc
+++ b/chrome/browser/ui/search/local_ntp_test_utils.cc
@@ -126,17 +126,4 @@
   template_url_service->SetUserSelectedDefaultSearchProvider(template_url);
 }
 
-GURL GetFinalNtpUrl(Profile* profile) {
-  if (search::GetNewTabPageURL(profile) == chrome::kChromeSearchLocalNtpUrl) {
-    // If chrome://newtab/ already maps to the local NTP, then that will load
-    // correctly, even without network.  The URL associated with the WebContents
-    // will stay chrome://newtab/
-    return GURL(chrome::kChromeUINewTabURL);
-  }
-  // If chrome://newtab/ maps to a remote URL, then it will fail to load in a
-  // browser_test environment.  In this case, we will get redirected to the
-  // local NTP, which changes the URL associated with the WebContents.
-  return GURL(chrome::kChromeSearchLocalNtpUrl);
-}
-
 }  // namespace local_ntp_test_utils
diff --git a/chrome/browser/ui/search/local_ntp_test_utils.h b/chrome/browser/ui/search/local_ntp_test_utils.h
index 1b63eb7..2962347 100644
--- a/chrome/browser/ui/search/local_ntp_test_utils.h
+++ b/chrome/browser/ui/search/local_ntp_test_utils.h
@@ -32,12 +32,6 @@
                                           const std::string& base_url,
                                           const std::string& ntp_url);
 
-// Get the URL that WebContents->GetVisibleURL() will return after navigating to
-// chrome://newtab/.  While this should typically be chrome://newtab/, in a test
-// environment where there is no network connection, it may be
-// chrome-search://local-ntp/local-ntp.html.
-GURL GetFinalNtpUrl(Profile* profile);
-
 }  // namespace local_ntp_test_utils
 
 #endif  // CHROME_BROWSER_UI_SEARCH_LOCAL_NTP_TEST_UTILS_H_
diff --git a/chrome/browser/ui/search/new_tab_page_interceptor_browsertest.cc b/chrome/browser/ui/search/new_tab_page_interceptor_browsertest.cc
new file mode 100644
index 0000000..0e1bdc7
--- /dev/null
+++ b/chrome/browser/ui/search/new_tab_page_interceptor_browsertest.cc
@@ -0,0 +1,111 @@
+// 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.
+
+#include <memory>
+
+#include "base/files/file_path.h"
+#include "base/strings/utf_string_conversions.h"
+#include "chrome/browser/browser_process.h"
+#include "chrome/browser/search/search.h"
+#include "chrome/browser/search_engines/template_url_service_factory.h"
+#include "chrome/browser/search_engines/ui_thread_search_terms_data.h"
+#include "chrome/browser/ui/browser.h"
+#include "chrome/browser/ui/tabs/tab_strip_model.h"
+#include "chrome/common/url_constants.h"
+#include "chrome/test/base/in_process_browser_test.h"
+#include "chrome/test/base/search_test_utils.h"
+#include "chrome/test/base/ui_test_utils.h"
+#include "components/search_engines/template_url_service.h"
+#include "content/public/browser/navigation_controller.h"
+#include "content/public/browser/navigation_entry.h"
+#include "content/public/browser/web_contents.h"
+#include "net/test/embedded_test_server/embedded_test_server.h"
+#include "net/test/url_request/url_request_mock_http_job.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "url/gurl.h"
+
+using content::BrowserThread;
+
+class NewTabPageInterceptorTest : public InProcessBrowserTest {
+ public:
+  NewTabPageInterceptorTest() {}
+
+  void SetUpOnMainThread() override {
+    base::FilePath path =
+        ui_test_utils::GetTestFilePath(base::FilePath(), base::FilePath());
+    BrowserThread::PostTask(
+        BrowserThread::IO, FROM_HERE,
+        base::BindOnce(&net::URLRequestMockHTTPJob::AddUrlHandlers, path));
+  }
+
+  void ChangeDefaultSearchProvider(const char* new_tab_path) {
+    TemplateURLService* template_url_service =
+        TemplateURLServiceFactory::GetForProfile(browser()->profile());
+    search_test_utils::WaitForTemplateURLServiceToLoad(template_url_service);
+    UIThreadSearchTermsData::SetGoogleBaseURL("https://mock.http/");
+    std::string base_url("{google:baseURL}");
+    TemplateURLData data;
+    data.SetShortName(base::ASCIIToUTF16("Google"));
+    data.SetKeyword(base::UTF8ToUTF16(base_url));
+    data.SetURL(base_url + "url?bar={searchTerms}");
+    data.new_tab_url = base_url + new_tab_path;
+    TemplateURL* template_url =
+        template_url_service->Add(std::make_unique<TemplateURL>(data));
+    template_url_service->SetUserSelectedDefaultSearchProvider(template_url);
+  }
+};
+
+IN_PROC_BROWSER_TEST_F(NewTabPageInterceptorTest, NoInterception) {
+  net::EmbeddedTestServer https_test_server(
+      net::EmbeddedTestServer::TYPE_HTTPS);
+  ASSERT_TRUE(https_test_server.Start());
+  GURL new_tab_url = https_test_server.GetURL("/instant_extended.html");
+  ChangeDefaultSearchProvider("instant_extended.html");
+
+  ui_test_utils::NavigateToURL(browser(), new_tab_url);
+  content::WebContents* contents =
+      browser()->tab_strip_model()->GetWebContentsAt(0);
+  // A correct, 200-OK file works correctly.
+  EXPECT_EQ(new_tab_url,
+            contents->GetController().GetLastCommittedEntry()->GetURL());
+}
+
+IN_PROC_BROWSER_TEST_F(NewTabPageInterceptorTest, 404Interception) {
+  GURL new_tab_url =
+      net::URLRequestMockHTTPJob::GetMockHttpsUrl("page404.html");
+  ChangeDefaultSearchProvider("page404.html");
+
+  ui_test_utils::NavigateToURL(browser(), new_tab_url);
+  content::WebContents* contents =
+      browser()->tab_strip_model()->GetWebContentsAt(0);
+  // 404 makes a redirect to the local NTP.
+  EXPECT_EQ(GURL(chrome::kChromeSearchLocalNtpUrl),
+            contents->GetController().GetLastCommittedEntry()->GetURL());
+}
+
+IN_PROC_BROWSER_TEST_F(NewTabPageInterceptorTest, 204Interception) {
+  GURL new_tab_url =
+      net::URLRequestMockHTTPJob::GetMockHttpsUrl("page204.html");
+  ChangeDefaultSearchProvider("page204.html");
+
+  ui_test_utils::NavigateToURL(browser(), new_tab_url);
+  content::WebContents* contents =
+      browser()->tab_strip_model()->GetWebContentsAt(0);
+  // 204 makes a redirect to the local NTP.
+  EXPECT_EQ(GURL(chrome::kChromeSearchLocalNtpUrl),
+            contents->GetController().GetLastCommittedEntry()->GetURL());
+}
+
+IN_PROC_BROWSER_TEST_F(NewTabPageInterceptorTest, FailedRequestInterception) {
+  GURL new_tab_url =
+      net::URLRequestMockHTTPJob::GetMockHttpsUrl("notarealfile.html");
+  ChangeDefaultSearchProvider("notarealfile.html");
+
+  ui_test_utils::NavigateToURL(browser(), new_tab_url);
+  content::WebContents* contents =
+      browser()->tab_strip_model()->GetWebContentsAt(0);
+  // Failed navigation makes a redirect to the local NTP.
+  EXPECT_EQ(GURL(chrome::kChromeSearchLocalNtpUrl),
+            contents->GetController().GetLastCommittedEntry()->GetURL());
+}
diff --git a/chrome/browser/ui/search/new_tab_page_interceptor_service.cc b/chrome/browser/ui/search/new_tab_page_interceptor_service.cc
new file mode 100644
index 0000000..44d2b031
--- /dev/null
+++ b/chrome/browser/ui/search/new_tab_page_interceptor_service.cc
@@ -0,0 +1,130 @@
+// 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.
+
+#include "chrome/browser/ui/search/new_tab_page_interceptor_service.h"
+
+#include <utility>
+
+#include "base/location.h"
+#include "base/logging.h"
+#include "base/macros.h"
+#include "base/metrics/histogram_macros.h"
+#include "chrome/browser/profiles/profile.h"
+#include "chrome/browser/search/search.h"
+#include "chrome/browser/search_engines/template_url_service_factory.h"
+#include "chrome/common/url_constants.h"
+#include "components/search_engines/template_url_service.h"
+#include "content/public/browser/browser_thread.h"
+#include "net/base/net_errors.h"
+#include "net/http/http_status_code.h"
+#include "net/url_request/url_request.h"
+#include "net/url_request/url_request_interceptor.h"
+#include "net/url_request/url_request_job.h"
+#include "net/url_request/url_request_redirect_job.h"
+#include "url/gurl.h"
+
+// Implementation of the URLRequestInterceptor for the New Tab Page. Will look
+// at incoming response from the server and possibly divert to the local NTP.
+class NewTabPageInterceptor : public net::URLRequestInterceptor {
+ public:
+  explicit NewTabPageInterceptor(const GURL& new_tab_url)
+      : new_tab_url_(new_tab_url), weak_factory_(this) {
+    DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
+  }
+
+  ~NewTabPageInterceptor() override {
+    DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
+  }
+
+  base::WeakPtr<NewTabPageInterceptor> GetWeakPtr() {
+    DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
+    return weak_factory_.GetWeakPtr();
+  }
+
+  void SetNewTabPageURL(const GURL& new_tab_page_url) {
+    DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
+    new_tab_url_ = new_tab_page_url;
+  }
+
+ private:
+  // Overrides from net::URLRequestInterceptor:
+  net::URLRequestJob* MaybeInterceptRequest(
+      net::URLRequest* request,
+      net::NetworkDelegate* network_delegate) const override {
+    DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
+    return nullptr;
+  }
+
+  net::URLRequestJob* MaybeInterceptResponse(
+      net::URLRequest* request,
+      net::NetworkDelegate* network_delegate) const override {
+    DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
+    if ((request->url() != new_tab_url_) ||
+        (new_tab_url_ == chrome::kChromeSearchLocalNtpUrl)) {
+      return nullptr;
+    }
+    // User has canceled this navigation so it shouldn't be redirected.
+    // TODO(maksims): Remove request->status() and use int net_error
+    // once MaybeInterceptResponse() starts to pass that.
+    if (request->status().status() == net::URLRequestStatus::CANCELED ||
+        (request->status().status() == net::URLRequestStatus::FAILED &&
+         request->status().error() == net::ERR_ABORTED)) {
+      return nullptr;
+    }
+
+    // Request to NTP was successful.
+    // TODO(maksims): Remove request->status() and use int net_error
+    // once MaybeInterceptResponse() starts to pass that.
+    if (request->status().is_success() &&
+        request->GetResponseCode() != net::HTTP_NO_CONTENT &&
+        request->GetResponseCode() < 400) {
+      return nullptr;
+    }
+
+    // Failure to load the NTP correctly; redirect to Local NTP.
+    UMA_HISTOGRAM_ENUMERATION("InstantExtended.CacheableNTPLoad",
+                              search::CACHEABLE_NTP_LOAD_FAILED,
+                              search::CACHEABLE_NTP_LOAD_MAX);
+    return new net::URLRequestRedirectJob(
+        request, network_delegate, GURL(chrome::kChromeSearchLocalNtpUrl),
+        net::URLRequestRedirectJob::REDIRECT_307_TEMPORARY_REDIRECT,
+        "NTP Request Interceptor");
+  }
+
+  GURL new_tab_url_;
+  base::WeakPtrFactory<NewTabPageInterceptor> weak_factory_;
+
+  DISALLOW_COPY_AND_ASSIGN(NewTabPageInterceptor);
+};
+
+NewTabPageInterceptorService::NewTabPageInterceptorService(Profile* profile)
+    : profile_(profile),
+      template_url_service_(TemplateURLServiceFactory::GetForProfile(profile)) {
+  DCHECK(profile_);
+  if (template_url_service_)
+    template_url_service_->AddObserver(this);
+}
+
+NewTabPageInterceptorService::~NewTabPageInterceptorService() {
+  if (template_url_service_)
+    template_url_service_->RemoveObserver(this);
+}
+
+void NewTabPageInterceptorService::OnTemplateURLServiceChanged() {
+  DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
+  GURL new_tab_page_url(search::GetNewTabPageURL(profile_));
+  content::BrowserThread::PostTask(
+      content::BrowserThread::IO, FROM_HERE,
+      base::BindOnce(&NewTabPageInterceptor::SetNewTabPageURL, interceptor_,
+                     new_tab_page_url));
+}
+
+std::unique_ptr<net::URLRequestInterceptor>
+NewTabPageInterceptorService::CreateInterceptor() {
+  DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
+  std::unique_ptr<NewTabPageInterceptor> interceptor(
+      new NewTabPageInterceptor(search::GetNewTabPageURL(profile_)));
+  interceptor_ = interceptor->GetWeakPtr();
+  return std::move(interceptor);
+}
diff --git a/chrome/browser/ui/search/new_tab_page_interceptor_service.h b/chrome/browser/ui/search/new_tab_page_interceptor_service.h
new file mode 100644
index 0000000..ff5f22c
--- /dev/null
+++ b/chrome/browser/ui/search/new_tab_page_interceptor_service.h
@@ -0,0 +1,46 @@
+// 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.
+
+#ifndef CHROME_BROWSER_UI_SEARCH_NEW_TAB_PAGE_INTERCEPTOR_SERVICE_H_
+#define CHROME_BROWSER_UI_SEARCH_NEW_TAB_PAGE_INTERCEPTOR_SERVICE_H_
+
+#include <memory>
+
+#include "base/macros.h"
+#include "base/memory/weak_ptr.h"
+#include "components/keyed_service/core/keyed_service.h"
+#include "components/search_engines/template_url_service_observer.h"
+
+class NewTabPageInterceptor;
+class Profile;
+class TemplateURLService;
+
+namespace net {
+class URLRequestInterceptor;
+}
+
+// Owns a NewTabPageInterceptor.
+class NewTabPageInterceptorService : public KeyedService,
+                                     public TemplateURLServiceObserver {
+ public:
+  explicit NewTabPageInterceptorService(Profile* profile);
+  ~NewTabPageInterceptorService() override;
+
+  // TemplateURLServiceObserver override.
+  void OnTemplateURLServiceChanged() override;
+
+  std::unique_ptr<net::URLRequestInterceptor> CreateInterceptor();
+
+ private:
+  Profile* profile_;
+  base::WeakPtr<NewTabPageInterceptor> interceptor_;
+  // The TemplateURLService that we are observing. It will outlive this
+  // NewTabPageInterceptorService due to the dependency declared in
+  // NewTabPageInterceptorServiceFactory.
+  TemplateURLService* template_url_service_;
+
+  DISALLOW_COPY_AND_ASSIGN(NewTabPageInterceptorService);
+};
+
+#endif  // CHROME_BROWSER_UI_SEARCH_NEW_TAB_PAGE_INTERCEPTOR_SERVICE_H_
diff --git a/chrome/browser/ui/search/new_tab_page_interceptor_service_factory.cc b/chrome/browser/ui/search/new_tab_page_interceptor_service_factory.cc
new file mode 100644
index 0000000..0aa11814
--- /dev/null
+++ b/chrome/browser/ui/search/new_tab_page_interceptor_service_factory.cc
@@ -0,0 +1,48 @@
+// 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.
+
+#include "chrome/browser/ui/search/new_tab_page_interceptor_service_factory.h"
+
+#include "chrome/browser/profiles/profile.h"
+#include "chrome/browser/search/instant_service_factory.h"
+#include "chrome/browser/search_engines/template_url_service_factory.h"
+#include "chrome/browser/ui/search/new_tab_page_interceptor_service.h"
+#include "components/keyed_service/content/browser_context_dependency_manager.h"
+#include "content/public/browser/browser_thread.h"
+
+// static
+NewTabPageInterceptorServiceFactory*
+NewTabPageInterceptorServiceFactory::GetInstance() {
+  DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
+  return base::Singleton<NewTabPageInterceptorServiceFactory>::get();
+}
+
+// static
+NewTabPageInterceptorService*
+NewTabPageInterceptorServiceFactory::GetForProfile(Profile* profile) {
+  DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
+  if (profile->IsOffTheRecord())
+    return nullptr;
+
+  return static_cast<NewTabPageInterceptorService*>(
+      GetInstance()->GetServiceForBrowserContext(profile, true));
+}
+
+NewTabPageInterceptorServiceFactory::NewTabPageInterceptorServiceFactory()
+    : BrowserContextKeyedServiceFactory(
+          "NTP Request Interceptor Service Factory",
+          BrowserContextDependencyManager::GetInstance()) {
+  DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
+  DependsOn(TemplateURLServiceFactory::GetInstance());
+}
+
+NewTabPageInterceptorServiceFactory::~NewTabPageInterceptorServiceFactory() {
+}
+
+KeyedService* NewTabPageInterceptorServiceFactory::BuildServiceInstanceFor(
+    content::BrowserContext* context) const {
+  DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
+  Profile* profile = Profile::FromBrowserContext(context);
+  return new NewTabPageInterceptorService(profile);
+}
diff --git a/chrome/browser/ui/search/new_tab_page_interceptor_service_factory.h b/chrome/browser/ui/search/new_tab_page_interceptor_service_factory.h
new file mode 100644
index 0000000..37dee82c
--- /dev/null
+++ b/chrome/browser/ui/search/new_tab_page_interceptor_service_factory.h
@@ -0,0 +1,41 @@
+// 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.
+
+#ifndef CHROME_BROWSER_UI_SEARCH_NEW_TAB_PAGE_INTERCEPTOR_SERVICE_FACTORY_H_
+#define CHROME_BROWSER_UI_SEARCH_NEW_TAB_PAGE_INTERCEPTOR_SERVICE_FACTORY_H_
+
+#include "base/macros.h"
+#include "base/memory/singleton.h"
+#include "components/keyed_service/content/browser_context_keyed_service_factory.h"
+#include "components/keyed_service/core/keyed_service.h"
+
+class NewTabPageInterceptorService;
+class Profile;
+
+namespace content {
+class BrowserContext;
+}
+
+// Owns and creates NewTabPageInterceptorService instances.
+class NewTabPageInterceptorServiceFactory
+    : public BrowserContextKeyedServiceFactory {
+ public:
+  static NewTabPageInterceptorService* GetForProfile(Profile* profile);
+  static NewTabPageInterceptorServiceFactory* GetInstance();
+
+ private:
+  friend struct base::DefaultSingletonTraits<
+      NewTabPageInterceptorServiceFactory>;
+
+  NewTabPageInterceptorServiceFactory();
+  ~NewTabPageInterceptorServiceFactory() override;
+
+  // BrowserContextKeyedServiceFactory:
+  KeyedService* BuildServiceInstanceFor(
+      content::BrowserContext* context) const override;
+
+  DISALLOW_COPY_AND_ASSIGN(NewTabPageInterceptorServiceFactory);
+};
+
+#endif  // CHROME_BROWSER_UI_SEARCH_NEW_TAB_PAGE_INTERCEPTOR_SERVICE_FACTORY_H_
diff --git a/chrome/browser/ui/search/new_tab_page_navigation_throttle.cc b/chrome/browser/ui/search/new_tab_page_navigation_throttle.cc
deleted file mode 100644
index b964991..0000000
--- a/chrome/browser/ui/search/new_tab_page_navigation_throttle.cc
+++ /dev/null
@@ -1,74 +0,0 @@
-// Copyright 2018 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "chrome/browser/ui/search/new_tab_page_navigation_throttle.h"
-
-#include "base/metrics/histogram_macros.h"
-#include "chrome/browser/profiles/profile.h"
-#include "chrome/browser/search/search.h"
-#include "chrome/common/url_constants.h"
-#include "content/public/browser/navigation_handle.h"
-#include "content/public/browser/web_contents.h"
-#include "net/http/http_status_code.h"
-#include "url/gurl.h"
-
-NewTabPageNavigationThrottle::NewTabPageNavigationThrottle(
-    content::NavigationHandle* navigation_handle)
-    : content::NavigationThrottle(navigation_handle) {}
-
-NewTabPageNavigationThrottle::~NewTabPageNavigationThrottle() = default;
-
-const char* NewTabPageNavigationThrottle::GetNameForLogging() {
-  return "NewTabPageNavigationThrottle";
-}
-
-// static
-std::unique_ptr<content::NavigationThrottle>
-NewTabPageNavigationThrottle::MaybeCreateThrottleFor(
-    content::NavigationHandle* handle) {
-  content::WebContents* web_contents = handle->GetWebContents();
-  Profile* profile =
-      Profile::FromBrowserContext(web_contents->GetBrowserContext());
-  if (web_contents->GetVisibleURL() != chrome::kChromeUINewTabURL ||
-      !search::IsInstantNTPURL(handle->GetURL(), profile) ||
-      handle->GetURL() == chrome::kChromeSearchLocalNtpUrl) {
-    return nullptr;
-  }
-
-  return std::make_unique<NewTabPageNavigationThrottle>(handle);
-}
-
-content::NavigationThrottle::ThrottleCheckResult
-NewTabPageNavigationThrottle::WillProcessResponse() {
-  const net::HttpResponseHeaders* headers =
-      navigation_handle()->GetResponseHeaders();
-  if (!headers)
-    return content::NavigationThrottle::PROCEED;
-
-  int response_code = headers->response_code();
-  if (response_code < 400 && response_code != net::HTTP_NO_CONTENT)
-    return content::NavigationThrottle::PROCEED;
-
-  return OpenLocalNewTabPage();
-}
-
-content::NavigationThrottle::ThrottleCheckResult
-NewTabPageNavigationThrottle::WillFailRequest() {
-  return OpenLocalNewTabPage();
-}
-
-content::NavigationThrottle::ThrottleCheckResult
-NewTabPageNavigationThrottle::OpenLocalNewTabPage() {
-  UMA_HISTOGRAM_ENUMERATION("InstantExtended.CacheableNTPLoad",
-                            search::CACHEABLE_NTP_LOAD_FAILED,
-                            search::CACHEABLE_NTP_LOAD_MAX);
-  navigation_handle()->GetWebContents()->OpenURL(
-      content::OpenURLParams(GURL(chrome::kChromeSearchLocalNtpUrl),
-                             navigation_handle()->GetReferrer(),
-                             navigation_handle()->GetFrameTreeNodeId(),
-                             WindowOpenDisposition::CURRENT_TAB,
-                             navigation_handle()->GetPageTransition(),
-                             false /* is_renderer_initiated */));
-  return content::NavigationThrottle::CANCEL_AND_IGNORE;
-}
diff --git a/chrome/browser/ui/search/new_tab_page_navigation_throttle.h b/chrome/browser/ui/search/new_tab_page_navigation_throttle.h
deleted file mode 100644
index be0498b..0000000
--- a/chrome/browser/ui/search/new_tab_page_navigation_throttle.h
+++ /dev/null
@@ -1,38 +0,0 @@
-// Copyright 2018 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef CHROME_BROWSER_UI_SEARCH_NEW_TAB_PAGE_NAVIGATION_THROTTLE_H_
-#define CHROME_BROWSER_UI_SEARCH_NEW_TAB_PAGE_NAVIGATION_THROTTLE_H_
-
-#include <memory>
-
-#include "content/public/browser/navigation_throttle.h"
-
-namespace content {
-class NavigationHandle;
-}  // namespace content
-
-// A NavigationThrottle that opens the local New Tab Page when there is any
-// issue opening the remote New Tab Page.
-class NewTabPageNavigationThrottle : public content::NavigationThrottle {
- public:
-  // Returns a NavigationThrottle when:
-  // - we are navigating to the new tab page, and
-  // - the main frame is pointed at the new tab URL.
-  static std::unique_ptr<content::NavigationThrottle> MaybeCreateThrottleFor(
-      content::NavigationHandle* handle);
-
-  explicit NewTabPageNavigationThrottle(content::NavigationHandle* handle);
-  ~NewTabPageNavigationThrottle() override;
-
-  // content::NavigationThrottle:
-  ThrottleCheckResult WillFailRequest() override;
-  ThrottleCheckResult WillProcessResponse() override;
-  const char* GetNameForLogging() override;
-
- private:
-  ThrottleCheckResult OpenLocalNewTabPage();
-};
-
-#endif  // CHROME_BROWSER_UI_SEARCH_NEW_TAB_PAGE_NAVIGATION_THROTTLE_H_
diff --git a/chrome/browser/ui/search/new_tab_page_navigation_throttle_browsertest.cc b/chrome/browser/ui/search/new_tab_page_navigation_throttle_browsertest.cc
deleted file mode 100644
index dc40271c..0000000
--- a/chrome/browser/ui/search/new_tab_page_navigation_throttle_browsertest.cc
+++ /dev/null
@@ -1,97 +0,0 @@
-// Copyright 2018 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "chrome/browser/search/search.h"
-#include "chrome/browser/search_engines/template_url_service_factory.h"
-#include "chrome/browser/ui/browser.h"
-#include "chrome/browser/ui/search/local_ntp_test_utils.h"
-#include "chrome/browser/ui/tabs/tab_strip_model.h"
-#include "chrome/common/chrome_paths.h"
-#include "chrome/common/url_constants.h"
-#include "chrome/test/base/in_process_browser_test.h"
-#include "chrome/test/base/search_test_utils.h"
-#include "chrome/test/base/ui_test_utils.h"
-#include "components/search_engines/template_url_service.h"
-#include "content/public/browser/navigation_entry.h"
-#include "content/public/browser/web_contents.h"
-#include "net/test/embedded_test_server/embedded_test_server.h"
-#include "url/gurl.h"
-
-namespace {
-
-class NewTabPageNavigationThrottleTest : public InProcessBrowserTest {
- public:
-  NewTabPageNavigationThrottleTest()
-      : https_test_server_(net::EmbeddedTestServer::TYPE_HTTPS) {
-    https_test_server()->AddDefaultHandlers(
-        base::FilePath(FILE_PATH_LITERAL("chrome/test/data")));
-  }
-
-  void SetNewTabPage(const std::string& ntp_url) {
-    // Set the new tab page.
-    local_ntp_test_utils::SetUserSelectedDefaultSearchProvider(
-        browser()->profile(), https_test_server()->base_url().spec(), ntp_url);
-
-    // Ensure we are using the newly set new_tab_url and won't be directed
-    // to the local new tab page.
-    TemplateURLService* service =
-        TemplateURLServiceFactory::GetForProfile(browser()->profile());
-    search_test_utils::WaitForTemplateURLServiceToLoad(service);
-    ASSERT_EQ(search::GetNewTabPageURL(browser()->profile()), ntp_url);
-  }
-
-  // Navigates to the New Tab Page and then returns the GURL that ultimately was
-  // navigated to.
-  GURL NavigateToNewTabPage() {
-    ui_test_utils::NavigateToURL(browser(), GURL(chrome::kChromeUINewTabURL));
-    content::WebContents* contents =
-        browser()->tab_strip_model()->GetWebContentsAt(0);
-    return contents->GetController().GetLastCommittedEntry()->GetURL();
-  }
-
-  net::EmbeddedTestServer* https_test_server() { return &https_test_server_; }
-
-  net::EmbeddedTestServer https_test_server_;
-};
-
-IN_PROC_BROWSER_TEST_F(NewTabPageNavigationThrottleTest, NoThrottle) {
-  ASSERT_TRUE(https_test_server()->Start());
-  std::string ntp_url =
-      https_test_server()->GetURL("/instant_extended.html").spec();
-  SetNewTabPage(ntp_url);
-  // A correct, 200-OK file works correctly.
-  EXPECT_EQ(ntp_url, NavigateToNewTabPage());
-}
-
-IN_PROC_BROWSER_TEST_F(NewTabPageNavigationThrottleTest,
-                       FailedRequestThrottle) {
-  ASSERT_TRUE(https_test_server()->Start());
-  SetNewTabPage(https_test_server()->GetURL("/instant_extended.html").spec());
-  ASSERT_TRUE(https_test_server()->ShutdownAndWaitUntilComplete());
-  // Failed navigation makes a redirect to the local NTP.
-  EXPECT_EQ(chrome::kChromeSearchLocalNtpUrl, NavigateToNewTabPage());
-}
-
-IN_PROC_BROWSER_TEST_F(NewTabPageNavigationThrottleTest, LocalNewTabPage) {
-  ASSERT_TRUE(https_test_server()->Start());
-  SetNewTabPage(chrome::kChromeSearchLocalNtpUrl);
-  // Already going to the local NTP, so we should arrive there as expected.
-  EXPECT_EQ(chrome::kChromeSearchLocalNtpUrl, NavigateToNewTabPage());
-}
-
-IN_PROC_BROWSER_TEST_F(NewTabPageNavigationThrottleTest, 404Throttle) {
-  ASSERT_TRUE(https_test_server()->Start());
-  SetNewTabPage(https_test_server()->GetURL("/page404.html").spec());
-  // 404 makes a redirect to the local NTP.
-  EXPECT_EQ(chrome::kChromeSearchLocalNtpUrl, NavigateToNewTabPage());
-}
-
-IN_PROC_BROWSER_TEST_F(NewTabPageNavigationThrottleTest, 204Throttle) {
-  ASSERT_TRUE(https_test_server()->Start());
-  SetNewTabPage(https_test_server()->GetURL("/page204.html").spec());
-  // 204 makes a redirect to the local NTP.
-  EXPECT_EQ(chrome::kChromeSearchLocalNtpUrl, NavigateToNewTabPage());
-}
-
-}  // namespace
diff --git a/chrome/browser/ui/startup/startup_browser_creator_browsertest.cc b/chrome/browser/ui/startup/startup_browser_creator_browsertest.cc
index 1386caf9..d013123 100644
--- a/chrome/browser/ui/startup/startup_browser_creator_browsertest.cc
+++ b/chrome/browser/ui/startup/startup_browser_creator_browsertest.cc
@@ -30,7 +30,6 @@
 #include "chrome/browser/profiles/profile_attributes_storage.h"
 #include "chrome/browser/profiles/profile_impl.h"
 #include "chrome/browser/profiles/profile_manager.h"
-#include "chrome/browser/search/search.h"
 #include "chrome/browser/sessions/session_restore.h"
 #include "chrome/browser/signin/signin_promo.h"
 #include "chrome/browser/ui/browser.h"
@@ -758,7 +757,8 @@
 
   // The new browser should have only the NTP.
   ASSERT_EQ(1, tab_strip->count());
-  EXPECT_TRUE(search::IsInstantNTP(tab_strip->GetWebContentsAt(0)));
+  EXPECT_EQ(GURL(chrome::kChromeUINewTabURL),
+            tab_strip->GetWebContentsAt(0)->GetURL());
 
   // profile_urls opened the urls.
   ASSERT_EQ(1u, chrome::GetBrowserCount(profile_urls));
@@ -871,7 +871,8 @@
 
   // The new browser should have only the NTP.
   ASSERT_EQ(1, tab_strip->count());
-  EXPECT_TRUE(search::IsInstantNTP(tab_strip->GetWebContentsAt(0)));
+  EXPECT_EQ(GURL(chrome::kChromeUINewTabURL),
+            tab_strip->GetWebContentsAt(0)->GetURL());
 
   EnsureRestoreUIWasShown(tab_strip->GetWebContentsAt(0));
 
@@ -881,7 +882,8 @@
   ASSERT_TRUE(new_browser);
   tab_strip = new_browser->tab_strip_model();
   ASSERT_EQ(1, tab_strip->count());
-  EXPECT_TRUE(search::IsInstantNTP(tab_strip->GetWebContentsAt(0)));
+  EXPECT_EQ(GURL(chrome::kChromeUINewTabURL),
+            tab_strip->GetWebContentsAt(0)->GetURL());
   EnsureRestoreUIWasShown(tab_strip->GetWebContentsAt(0));
 
   // The profile which normally opens URLs displays the new tab page.
@@ -890,7 +892,8 @@
   ASSERT_TRUE(new_browser);
   tab_strip = new_browser->tab_strip_model();
   ASSERT_EQ(1, tab_strip->count());
-  EXPECT_TRUE(search::IsInstantNTP(tab_strip->GetWebContentsAt(0)));
+  EXPECT_EQ(GURL(chrome::kChromeUINewTabURL),
+            tab_strip->GetWebContentsAt(0)->GetURL());
   EnsureRestoreUIWasShown(tab_strip->GetWebContentsAt(0));
 
 #if !defined(OS_MACOSX) && !defined(GOOGLE_CHROME_BUILD)
diff --git a/chrome/browser/ui/toolbar/media_router_contextual_menu_unittest.cc b/chrome/browser/ui/toolbar/media_router_contextual_menu_unittest.cc
index 6597b35..d35424b4 100644
--- a/chrome/browser/ui/toolbar/media_router_contextual_menu_unittest.cc
+++ b/chrome/browser/ui/toolbar/media_router_contextual_menu_unittest.cc
@@ -8,6 +8,8 @@
 #include "chrome/app/chrome_command_ids.h"
 #include "chrome/browser/extensions/extension_action_test_util.h"
 #include "chrome/browser/extensions/load_error_reporter.h"
+#include "chrome/browser/media/router/media_router_factory.h"
+#include "chrome/browser/media/router/test/mock_media_router.h"
 #include "chrome/browser/signin/fake_signin_manager_builder.h"
 #include "chrome/browser/signin/signin_manager_factory.h"
 #include "chrome/browser/ui/extensions/browser_action_test_util.h"
@@ -63,6 +65,11 @@
     BrowserWithTestWindowTest::TearDown();
   }
 
+  TestingProfile::TestingFactories GetTestingFactories() override {
+    return {{media_router::MediaRouterFactory::GetInstance(),
+             &media_router::MockMediaRouter::Create}};
+  }
+
  protected:
   // These constants are used to inject the state of the Media Router action
   // that would be inferred in the production code.
diff --git a/chrome/browser/ui/views/folder_upload_confirmation_view.cc b/chrome/browser/ui/views/folder_upload_confirmation_view.cc
new file mode 100644
index 0000000..b9c3bb71
--- /dev/null
+++ b/chrome/browser/ui/views/folder_upload_confirmation_view.cc
@@ -0,0 +1,103 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/ui/views/folder_upload_confirmation_view.h"
+
+#include "base/files/file_path.h"
+#include "base/numerics/safe_conversions.h"
+#include "chrome/browser/file_select_helper.h"
+#include "chrome/browser/ui/views/harmony/chrome_layout_provider.h"
+#include "chrome/browser/ui/views/harmony/chrome_typography.h"
+#include "chrome/grit/generated_resources.h"
+#include "components/constrained_window/constrained_window_views.h"
+#include "ui/base/l10n/l10n_util.h"
+#include "ui/views/controls/label.h"
+#include "ui/views/layout/fill_layout.h"
+
+FolderUploadConfirmationView::FolderUploadConfirmationView(
+    const base::FilePath& path,
+    base::OnceCallback<void(const std::vector<ui::SelectedFileInfo>&)> callback,
+    std::vector<ui::SelectedFileInfo> selected_files)
+    : callback_(std::move(callback)),
+      selected_files_(std::move(selected_files)) {
+  SetLayoutManager(std::make_unique<views::FillLayout>());
+  auto label = std::make_unique<views::Label>(
+      l10n_util::GetStringFUTF16(IDS_CONFIRM_FILE_UPLOAD_TEXT,
+                                 path.BaseName().LossyDisplayName()),
+      CONTEXT_BODY_TEXT_LARGE, STYLE_SECONDARY);
+  label->SetMultiLine(true);
+  label->SetHorizontalAlignment(gfx::ALIGN_LEFT);
+  AddChildView(label.release());
+  set_margins(ChromeLayoutProvider::Get()->GetDialogInsetsForContentType(
+      views::TEXT, views::TEXT));
+}
+
+FolderUploadConfirmationView::~FolderUploadConfirmationView() {
+  // Make sure the dialog ends up calling the callback no matter what as
+  // FileSelectHelper keeps itself alive until it sends the result.
+  if (!callback_.is_null())
+    Cancel();
+}
+
+views::Widget* FolderUploadConfirmationView::ShowDialog(
+    const base::FilePath& path,
+    base::OnceCallback<void(const std::vector<ui::SelectedFileInfo>&)> callback,
+    std::vector<ui::SelectedFileInfo> selected_files,
+    content::WebContents* web_contents) {
+  auto delegate = std::make_unique<FolderUploadConfirmationView>(
+      path, std::move(callback), std::move(selected_files));
+  return constrained_window::ShowWebModalDialogViews(delegate.release(),
+                                                     web_contents);
+}
+
+base::string16 FolderUploadConfirmationView::GetWindowTitle() const {
+  return l10n_util::GetPluralStringFUTF16(
+      IDS_CONFIRM_FILE_UPLOAD_TITLE,
+      base::saturated_cast<int>(selected_files_.size()));
+}
+
+int FolderUploadConfirmationView::GetDefaultDialogButton() const {
+  return ui::DIALOG_BUTTON_CANCEL;
+}
+
+base::string16 FolderUploadConfirmationView::GetDialogButtonLabel(
+    ui::DialogButton button) const {
+  if (button == ui::DIALOG_BUTTON_OK)
+    return l10n_util::GetStringUTF16(IDS_CONFIRM_FILE_UPLOAD_OK_BUTTON);
+  return DialogDelegateView::GetDialogButtonLabel(button);
+}
+
+bool FolderUploadConfirmationView::ShouldShowCloseButton() const {
+  return false;
+}
+
+bool FolderUploadConfirmationView::Accept() {
+  std::move(callback_).Run(selected_files_);
+  return true;
+}
+
+bool FolderUploadConfirmationView::Cancel() {
+  std::move(callback_).Run(std::vector<ui::SelectedFileInfo>());
+  return true;
+}
+
+gfx::Size FolderUploadConfirmationView::CalculatePreferredSize() const {
+  const int width = ChromeLayoutProvider::Get()->GetDistanceMetric(
+                        DISTANCE_MODAL_DIALOG_PREFERRED_WIDTH) -
+                    margins().width();
+  return gfx::Size(width, GetHeightForWidth(width));
+}
+
+ui::ModalType FolderUploadConfirmationView::GetModalType() const {
+  return ui::MODAL_TYPE_CHILD;
+}
+
+void ShowFolderUploadConfirmationDialog(
+    const base::FilePath& path,
+    base::OnceCallback<void(const std::vector<ui::SelectedFileInfo>&)> callback,
+    std::vector<ui::SelectedFileInfo> selected_files,
+    content::WebContents* web_contents) {
+  FolderUploadConfirmationView::ShowDialog(
+      path, std::move(callback), std::move(selected_files), web_contents);
+}
diff --git a/chrome/browser/ui/views/folder_upload_confirmation_view.h b/chrome/browser/ui/views/folder_upload_confirmation_view.h
new file mode 100644
index 0000000..81f710e
--- /dev/null
+++ b/chrome/browser/ui/views/folder_upload_confirmation_view.h
@@ -0,0 +1,72 @@
+// 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 CHROME_BROWSER_UI_VIEWS_FOLDER_UPLOAD_CONFIRMATION_VIEW_H_
+#define CHROME_BROWSER_UI_VIEWS_FOLDER_UPLOAD_CONFIRMATION_VIEW_H_
+
+#include "base/macros.h"
+#include "base/strings/string16.h"
+#include "ui/shell_dialogs/selected_file_info.h"
+#include "ui/views/window/dialog_delegate.h"
+
+namespace base {
+class FilePath;
+}
+
+namespace content {
+class WebContents;
+}  // namespace content
+
+namespace views {
+class Widget;
+}  // namespace views
+
+// A dialog that confirms that the user intended to upload the specific folder.
+// The dialog provides information about how many files are about to be uploaded
+// as well as the path to it and cautions the user to only upload to sites that
+// they trust with the files. This is also a security measure against sites that
+// trick a user into pressing enter, which would instantly confirm the OS folder
+// picker and share the default folder selection without explicit user approval.
+class FolderUploadConfirmationView : public views::DialogDelegateView {
+ public:
+  FolderUploadConfirmationView(
+      const base::FilePath& path,
+      base::OnceCallback<void(const std::vector<ui::SelectedFileInfo>&)>
+          callback,
+      std::vector<ui::SelectedFileInfo> selected_files);
+  ~FolderUploadConfirmationView() override;
+
+  static views::Widget* ShowDialog(
+      const base::FilePath& path,
+      base::OnceCallback<void(const std::vector<ui::SelectedFileInfo>&)>
+          callback,
+      std::vector<ui::SelectedFileInfo> selected_files,
+      content::WebContents* web_contents);
+
+  // views::DialogDelegateView:
+  base::string16 GetWindowTitle() const override;
+
+  // It's really important that this dialog *does not* accept by default /
+  // when a user presses enter without looking as we're looking for explicit
+  // approval to share this many files with the site.
+  int GetDefaultDialogButton() const override;
+  base::string16 GetDialogButtonLabel(ui::DialogButton button) const override;
+
+  bool ShouldShowCloseButton() const override;
+
+  bool Accept() override;
+  bool Cancel() override;
+
+  gfx::Size CalculatePreferredSize() const override;
+
+  ui::ModalType GetModalType() const override;
+
+ private:
+  base::OnceCallback<void(const std::vector<ui::SelectedFileInfo>&)> callback_;
+  std::vector<ui::SelectedFileInfo> selected_files_;
+
+  DISALLOW_COPY_AND_ASSIGN(FolderUploadConfirmationView);
+};
+
+#endif  // CHROME_BROWSER_UI_VIEWS_FOLDER_UPLOAD_CONFIRMATION_VIEW_H_
diff --git a/chrome/browser/ui/views/folder_upload_confirmation_view_browsertest.cc b/chrome/browser/ui/views/folder_upload_confirmation_view_browsertest.cc
new file mode 100644
index 0000000..bc7a0cf
--- /dev/null
+++ b/chrome/browser/ui/views/folder_upload_confirmation_view_browsertest.cc
@@ -0,0 +1,96 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/ui/views/folder_upload_confirmation_view.h"
+
+#include "base/files/file_path.h"
+#include "base/strings/utf_string_conversions.h"
+#include "chrome/browser/ui/browser.h"
+#include "chrome/browser/ui/test/test_browser_dialog.h"
+#include "content/public/test/test_utils.h"
+#include "ui/views/controls/button/label_button.h"
+#include "ui/views/window/dialog_client_view.h"
+
+constexpr size_t kTestFileCount = 3;
+static const base::FilePath::StringPieceType kTestFileNames[kTestFileCount] = {
+    FILE_PATH_LITERAL("a.txt"), FILE_PATH_LITERAL("b.txt"),
+    FILE_PATH_LITERAL("c.txt")};
+
+class FolderUploadConfirmationViewTest : public DialogBrowserTest {
+ public:
+  FolderUploadConfirmationViewTest() {
+    for (size_t i = 0; i < kTestFileCount; ++i) {
+      ui::SelectedFileInfo file_info;
+      file_info.file_path = base::FilePath(kTestFileNames[i]);
+      test_files_.push_back(file_info);
+    }
+  }
+
+  // DialogBrowserTest:
+  void ShowUi(const std::string& name) override {
+    widget_ = FolderUploadConfirmationView::ShowDialog(
+        base::FilePath(FILE_PATH_LITERAL("Desktop")),
+        base::BindOnce(&FolderUploadConfirmationViewTest::Callback,
+                       base::Unretained(this)),
+        test_files_, browser()->tab_strip_model()->GetActiveWebContents());
+    content::RunAllPendingInMessageLoop();
+  }
+
+  void Callback(const std::vector<ui::SelectedFileInfo>& files) {
+    EXPECT_FALSE(callback_called_);
+    callback_called_ = true;
+    callback_files_ = files;
+  }
+
+ protected:
+  std::vector<ui::SelectedFileInfo> test_files_;
+
+  views::Widget* widget_ = nullptr;
+
+  bool callback_called_ = false;
+  std::vector<ui::SelectedFileInfo> callback_files_;
+
+  DISALLOW_COPY_AND_ASSIGN(FolderUploadConfirmationViewTest);
+};
+
+IN_PROC_BROWSER_TEST_F(FolderUploadConfirmationViewTest,
+                       InitiallyFocusesCancel) {
+  ShowUi(std::string());
+  EXPECT_EQ(widget_->client_view()->AsDialogClientView()->cancel_button(),
+            widget_->GetFocusManager()->GetFocusedView());
+  widget_->Close();
+  content::RunAllPendingInMessageLoop();
+}
+
+IN_PROC_BROWSER_TEST_F(FolderUploadConfirmationViewTest,
+                       AcceptRunsCallbackWithFileInfo) {
+  ShowUi(std::string());
+  widget_->client_view()->AsDialogClientView()->AcceptWindow();
+  EXPECT_TRUE(callback_called_);
+  ASSERT_EQ(kTestFileCount, callback_files_.size());
+  for (size_t i = 0; i < kTestFileCount; ++i)
+    EXPECT_EQ(test_files_[i].file_path, callback_files_[i].file_path);
+  content::RunAllPendingInMessageLoop();
+}
+
+IN_PROC_BROWSER_TEST_F(FolderUploadConfirmationViewTest,
+                       CancelRunsCallbackWithEmptyFileInfo) {
+  ShowUi(std::string());
+  widget_->client_view()->AsDialogClientView()->CancelWindow();
+  EXPECT_TRUE(callback_called_);
+  EXPECT_TRUE(callback_files_.empty());
+  content::RunAllPendingInMessageLoop();
+}
+
+IN_PROC_BROWSER_TEST_F(FolderUploadConfirmationViewTest, CancelsWhenClosed) {
+  ShowUi(std::string());
+  widget_->Close();
+  EXPECT_TRUE(callback_called_);
+  EXPECT_TRUE(callback_files_.empty());
+  content::RunAllPendingInMessageLoop();
+}
+
+IN_PROC_BROWSER_TEST_F(FolderUploadConfirmationViewTest, InvokeUi_default) {
+  ShowAndVerifyUi();
+}
diff --git a/chrome/browser/ui/views/frame/browser_non_client_frame_view_ash.cc b/chrome/browser/ui/views/frame/browser_non_client_frame_view_ash.cc
index 881501f..0183c1d 100644
--- a/chrome/browser/ui/views/frame/browser_non_client_frame_view_ash.cc
+++ b/chrome/browser/ui/views/frame/browser_non_client_frame_view_ash.cc
@@ -15,7 +15,11 @@
 #include "ash/frame/frame_header_util.h"
 #include "ash/public/cpp/app_types.h"
 #include "ash/public/cpp/ash_switches.h"
+#include "ash/public/cpp/window_properties.h"
+#include "ash/public/interfaces/constants.mojom.h"
+#include "ash/public/interfaces/window_state_type.mojom.h"
 #include "ash/shell.h"
+#include "ash/wm/overview/window_selector_controller.h"
 #include "ash/wm/window_util.h"
 #include "base/command_line.h"
 #include "chrome/app/chrome_command_ids.h"
@@ -37,6 +41,8 @@
 #include "chrome/browser/ui/views/toolbar/toolbar_view.h"
 #include "chrome/browser/web_applications/web_app.h"
 #include "content/public/browser/web_contents.h"
+#include "content/public/common/service_manager_connection.h"
+#include "services/service_manager/public/cpp/connector.h"
 #include "third_party/skia/include/core/SkColor.h"
 #include "ui/accessibility/ax_node_data.h"
 #include "ui/aura/client/aura_constants.h"
@@ -66,6 +72,27 @@
       ash::switches::kAshEnableV1AppBackButton);
 }
 
+// Returns true if |window| is currently snapped in split view mode.
+bool IsSnappedInSplitView(aura::Window* window,
+                          ash::mojom::SplitViewState state) {
+  ash::mojom::WindowStateType type =
+      window->GetProperty(ash::kWindowStateTypeKey);
+  switch (state) {
+    case ash::mojom::SplitViewState::NO_SNAP:
+      return false;
+    case ash::mojom::SplitViewState::LEFT_SNAPPED:
+      return type == ash::mojom::WindowStateType::LEFT_SNAPPED;
+    case ash::mojom::SplitViewState::RIGHT_SNAPPED:
+      return type == ash::mojom::WindowStateType::RIGHT_SNAPPED;
+    case ash::mojom::SplitViewState::BOTH_SNAPPED:
+      return type == ash::mojom::WindowStateType::LEFT_SNAPPED ||
+             type == ash::mojom::WindowStateType::RIGHT_SNAPPED;
+    default:
+      NOTREACHED();
+      return false;
+  }
+}
+
 }  // namespace
 
 ///////////////////////////////////////////////////////////////////////////////
@@ -78,10 +105,21 @@
       caption_button_container_(nullptr),
       back_button_(nullptr),
       window_icon_(nullptr),
-      hosted_app_button_container_(nullptr) {
+      hosted_app_button_container_(nullptr),
+      observer_binding_(this) {
   ash::wm::InstallResizeHandleWindowTargeterForWindow(frame->GetNativeWindow(),
                                                       nullptr);
   ash::Shell::Get()->AddShellObserver(this);
+
+  // The ServiceManagerConnection may be nullptr in tests.
+  if (content::ServiceManagerConnection::GetForProcess()) {
+    content::ServiceManagerConnection::GetForProcess()
+        ->GetConnector()
+        ->BindInterface(ash::mojom::kServiceName, &split_view_controller_);
+    ash::mojom::SplitViewObserverPtr observer;
+    observer_binding_.Bind(mojo::MakeRequest(&observer));
+    split_view_controller_->AddObserver(std::move(observer));
+  }
 }
 
 BrowserNonClientFrameViewAsh::~BrowserNonClientFrameViewAsh() {
@@ -130,6 +168,15 @@
     TabletModeClient::Get()->AddObserver(this);
 }
 
+ash::mojom::SplitViewObserverPtr
+BrowserNonClientFrameViewAsh::CreateInterfacePtrForTesting() {
+  if (observer_binding_.is_bound())
+    observer_binding_.Unbind();
+  ash::mojom::SplitViewObserverPtr ptr;
+  observer_binding_.Bind(mojo::MakeRequest(&ptr));
+  return ptr;
+}
+
 ///////////////////////////////////////////////////////////////////////////////
 // BrowserNonClientFrameView:
 
@@ -358,6 +405,8 @@
 // ash::ShellObserver:
 
 void BrowserNonClientFrameViewAsh::OnOverviewModeStarting() {
+  in_overview_mode_ = true;
+
   // Update the window icon so that overview mode can grab the icon from
   // aura::client::kWindowIcon to display.
   if (base::CommandLine::ForCurrentProcess()->HasSwitch(
@@ -367,11 +416,12 @@
 
   frame()->GetNativeWindow()->SetProperty(aura::client::kTopViewColor,
                                           GetFrameColor());
-  OnOverviewModeChanged(true);
+  OnOverviewOrSplitviewModeChanged();
 }
 
 void BrowserNonClientFrameViewAsh::OnOverviewModeEnded() {
-  OnOverviewModeChanged(false);
+  in_overview_mode_ = false;
+  OnOverviewOrSplitviewModeChanged();
 }
 
 ///////////////////////////////////////////////////////////////////////////////
@@ -438,6 +488,12 @@
     back_button_->SetEnabled(enabled);
 }
 
+void BrowserNonClientFrameViewAsh::OnSplitViewStateChanged(
+    ash::mojom::SplitViewState current_state) {
+  split_view_state_ = current_state;
+  OnOverviewOrSplitviewModeChanged();
+}
+
 ///////////////////////////////////////////////////////////////////////////////
 // BrowserNonClientFrameViewAsh, protected:
 
@@ -506,13 +562,21 @@
   return browser_view()->IsBrowserTypeNormal() || !in_overview_mode_;
 }
 
-void BrowserNonClientFrameViewAsh::OnOverviewModeChanged(bool in_overview) {
-  in_overview_mode_ = in_overview;
-  caption_button_container_->SetVisible(!in_overview);
-  if (window_icon_)
-    window_icon_->SetVisible(!in_overview);
-  if (back_button_)
-    back_button_->SetVisible(!in_overview);
+void BrowserNonClientFrameViewAsh::OnOverviewOrSplitviewModeChanged() {
+  if (in_overview_mode_ &&
+      IsSnappedInSplitView(frame()->GetNativeWindow(), split_view_state_)) {
+    caption_button_container_->SetVisible(true);
+    if (window_icon_)
+      window_icon_->SetVisible(true);
+    if (back_button_)
+      back_button_->SetVisible(true);
+  } else {
+    caption_button_container_->SetVisible(!in_overview_mode_);
+    if (window_icon_)
+      window_icon_->SetVisible(!in_overview_mode_);
+    if (back_button_)
+      back_button_->SetVisible(!in_overview_mode_);
+  }
   // Schedule a paint to show or hide the header.
   SchedulePaint();
 }
diff --git a/chrome/browser/ui/views/frame/browser_non_client_frame_view_ash.h b/chrome/browser/ui/views/frame/browser_non_client_frame_view_ash.h
index 76d527e..80ed3f8 100644
--- a/chrome/browser/ui/views/frame/browser_non_client_frame_view_ash.h
+++ b/chrome/browser/ui/views/frame/browser_non_client_frame_view_ash.h
@@ -7,6 +7,7 @@
 
 #include <memory>
 
+#include "ash/public/interfaces/split_view.mojom.h"
 #include "ash/shell_observer.h"
 #include "base/gtest_prod_util.h"
 #include "base/macros.h"
@@ -14,6 +15,7 @@
 #include "chrome/browser/ui/ash/tablet_mode_client_observer.h"
 #include "chrome/browser/ui/views/frame/browser_non_client_frame_view.h"
 #include "chrome/browser/ui/views/tab_icon_view_model.h"
+#include "mojo/public/cpp/bindings/binding.h"
 
 class HostedAppButtonContainer;
 class TabIconView;
@@ -29,13 +31,16 @@
                                      public ash::ShellObserver,
                                      public TabletModeClientObserver,
                                      public TabIconViewModel,
-                                     public CommandObserver {
+                                     public CommandObserver,
+                                     public ash::mojom::SplitViewObserver {
  public:
   BrowserNonClientFrameViewAsh(BrowserFrame* frame, BrowserView* browser_view);
   ~BrowserNonClientFrameViewAsh() override;
 
   void Init();
 
+  ash::mojom::SplitViewObserverPtr CreateInterfacePtrForTesting();
+
   // BrowserNonClientFrameView:
   gfx::Rect GetBoundsForTabStrip(views::View* tabstrip) const override;
   int GetTopInset(bool restored) const override;
@@ -77,12 +82,15 @@
   // CommandObserver:
   void EnabledStateChangedForCommand(int id, bool enabled) override;
 
+  // ash::mojom::SplitViewObserver:
+  void OnSplitViewStateChanged(
+      ash::mojom::SplitViewState current_state) override;
+
  protected:
   // BrowserNonClientFrameView:
   void UpdateProfileIcons() override;
 
  private:
-  FRIEND_TEST_ALL_PREFIXES(BrowserNonClientFrameViewAshTest, WindowHeader);
   FRIEND_TEST_ALL_PREFIXES(BrowserNonClientFrameViewAshTest,
                            NonImmersiveFullscreen);
   FRIEND_TEST_ALL_PREFIXES(BrowserNonClientFrameViewAshTest,
@@ -91,6 +99,8 @@
                            ToggleTabletModeRelayout);
   FRIEND_TEST_ALL_PREFIXES(BrowserNonClientFrameViewAshTest,
                            AvatarDisplayOnTeleportedWindow);
+  FRIEND_TEST_ALL_PREFIXES(BrowserNonClientFrameViewAshTest,
+                           HeaderVisibilityInOverviewAndSplitview);
   FRIEND_TEST_ALL_PREFIXES(HostedAppNonClientFrameViewAshTest, HostedAppFrame);
   FRIEND_TEST_ALL_PREFIXES(BrowserNonClientFrameViewAshBackButtonTest,
                            V1BackButton);
@@ -118,8 +128,8 @@
   bool ShouldPaint() const;
 
   // Helps to hide or show the header as needed when overview mode starts or
-  // ends.
-  void OnOverviewModeChanged(bool in_overview);
+  // ends or when split view state changes.
+  void OnOverviewOrSplitviewModeChanged();
 
   // Creates the frame header for the browser window.
   std::unique_ptr<ash::FrameHeader> CreateFrameHeader();
@@ -139,12 +149,22 @@
   // Owned by views hierarchy.
   HostedAppButtonContainer* hosted_app_button_container_;
 
+  // Ash's mojom::SplitViewController.
+  ash::mojom::SplitViewControllerPtr split_view_controller_;
+
+  // The binding this instance uses to implement mojom::SplitViewObserver.
+  mojo::Binding<ash::mojom::SplitViewObserver> observer_binding_;
+
   // Indicates whether overview mode is active. Hide the header for V1 apps in
   // overview mode because a fake header is added for better UX. If also in
   // immersive mode before entering overview mode, the flag will be ignored
   // because the reveal lock will determine the show/hide header.
   bool in_overview_mode_ = false;
 
+  // Maintains the current split view state.
+  ash::mojom::SplitViewState split_view_state_ =
+      ash::mojom::SplitViewState::NO_SNAP;
+
   DISALLOW_COPY_AND_ASSIGN(BrowserNonClientFrameViewAsh);
 };
 
diff --git a/chrome/browser/ui/views/frame/browser_non_client_frame_view_ash_browsertest.cc b/chrome/browser/ui/views/frame/browser_non_client_frame_view_ash_browsertest.cc
index 4fbb663..94e779f 100644
--- a/chrome/browser/ui/views/frame/browser_non_client_frame_view_ash_browsertest.cc
+++ b/chrome/browser/ui/views/frame/browser_non_client_frame_view_ash_browsertest.cc
@@ -15,6 +15,7 @@
 #include "ash/public/cpp/immersive/immersive_fullscreen_controller_test_api.h"
 #include "ash/shell.h"
 #include "ash/wm/overview/window_selector_controller.h"
+#include "ash/wm/splitview/split_view_controller.h"
 #include "ash/wm/tablet_mode/tablet_mode_controller.h"
 #include "base/command_line.h"
 #include "base/run_loop.h"
@@ -50,8 +51,10 @@
 #include "components/signin/core/account_id/account_id.h"
 #include "content/public/browser/render_frame_host.h"
 #include "content/public/browser/render_process_host.h"
+#include "services/ui/public/interfaces/window_manager_constants.mojom.h"
 #include "ui/aura/client/aura_constants.h"
 #include "ui/aura/test/env_test_helper.h"
+#include "ui/base/class_property.h"
 #include "ui/base/hit_test.h"
 #include "ui/base/ui_base_switches.h"
 #include "ui/compositor/scoped_animation_duration_scale_mode.h"
@@ -672,6 +675,86 @@
   EXPECT_EQ(0, window->GetProperty(aura::client::kTopViewInset));
 }
 
+IN_PROC_BROWSER_TEST_P(BrowserNonClientFrameViewAshTest,
+                       HeaderVisibilityInOverviewAndSplitview) {
+  BrowserView* browser_view = BrowserView::GetBrowserViewForBrowser(browser());
+  Widget* widget = browser_view->GetWidget();
+  BrowserNonClientFrameViewAsh* frame_view =
+      static_cast<BrowserNonClientFrameViewAsh*>(
+          widget->non_client_view()->frame_view());
+  widget->GetNativeWindow()->SetProperty(
+      aura::client::kResizeBehaviorKey,
+      ui::mojom::kResizeBehaviorCanMaximize |
+          ui::mojom::kResizeBehaviorCanResize);
+
+  // Test that the header is invisible for the browser window in overview mode
+  // and visible when not in overview mode.
+  ash::Shell* shell = ash::Shell::Get();
+  shell->window_selector_controller()->ToggleOverview();
+  EXPECT_FALSE(frame_view->caption_button_container_->visible());
+  shell->window_selector_controller()->ToggleOverview();
+  EXPECT_TRUE(frame_view->caption_button_container_->visible());
+
+  // Create another browser window.
+  Browser::CreateParams params = Browser::CreateParams::CreateForApp(
+      "test_browser_app", true /* trusted_source */, gfx::Rect(),
+      browser()->profile(), true);
+  params.initial_show_state = ui::SHOW_STATE_DEFAULT;
+  Browser* browser2 = new Browser(params);
+  AddBlankTabAndShow(browser2);
+  BrowserView* browser_view2 = BrowserView::GetBrowserViewForBrowser(browser2);
+  Widget* widget2 = browser_view2->GetWidget();
+  BrowserNonClientFrameViewAsh* frame_view2 =
+      static_cast<BrowserNonClientFrameViewAsh*>(
+          widget2->non_client_view()->frame_view());
+  widget2->GetNativeWindow()->SetProperty(
+      aura::client::kResizeBehaviorKey,
+      ui::mojom::kResizeBehaviorCanMaximize |
+          ui::mojom::kResizeBehaviorCanResize);
+
+  // Test that when one browser window is snapped, the header is visible for the
+  // snapped browser window, but invisible for the browser window still in
+  // overview mode.
+  shell->tablet_mode_controller()->EnableTabletModeWindowManager(true);
+  ash::SplitViewController* split_view_controller =
+      shell->split_view_controller();
+  split_view_controller->BindRequest(
+      mojo::MakeRequest(&frame_view->split_view_controller_));
+  split_view_controller->BindRequest(
+      mojo::MakeRequest(&frame_view2->split_view_controller_));
+  split_view_controller->AddObserver(
+      frame_view->CreateInterfacePtrForTesting());
+  split_view_controller->AddObserver(
+      frame_view2->CreateInterfacePtrForTesting());
+  frame_view->split_view_controller_.FlushForTesting();
+  frame_view2->split_view_controller_.FlushForTesting();
+
+  shell->window_selector_controller()->ToggleOverview();
+  split_view_controller->SnapWindow(widget->GetNativeWindow(),
+                                    ash::SplitViewController::LEFT);
+  frame_view->split_view_controller_.FlushForTesting();
+  frame_view2->split_view_controller_.FlushForTesting();
+  EXPECT_TRUE(frame_view->caption_button_container_->visible());
+  EXPECT_FALSE(frame_view2->caption_button_container_->visible());
+
+  // When both browser windows are snapped, the headers are both visible.
+  split_view_controller->SnapWindow(widget2->GetNativeWindow(),
+                                    ash::SplitViewController::RIGHT);
+  frame_view->split_view_controller_.FlushForTesting();
+  frame_view2->split_view_controller_.FlushForTesting();
+  EXPECT_TRUE(frame_view->caption_button_container_->visible());
+  EXPECT_TRUE(frame_view2->caption_button_container_->visible());
+
+  // Toggle overview mode while splitview mode is active. Test that the header
+  // is visible for the snapped browser window but not for the other browser
+  // window in overview mode.
+  shell->window_selector_controller()->ToggleOverview();
+  frame_view->split_view_controller_.FlushForTesting();
+  frame_view2->split_view_controller_.FlushForTesting();
+  EXPECT_TRUE(frame_view->caption_button_container_->visible());
+  EXPECT_FALSE(frame_view2->caption_button_container_->visible());
+}
+
 // Test the V1 apps' kTopViewInset.
 IN_PROC_BROWSER_TEST_P(HostedAppNonClientFrameViewAshTest, V1AppTopViewInset) {
   browser()->window()->Close();
diff --git a/chrome/browser/ui/views/frame/native_browser_frame_factory_ozone.cc b/chrome/browser/ui/views/frame/native_browser_frame_factory_ozone.cc
index 9d14a94..e1ab238f 100644
--- a/chrome/browser/ui/views/frame/native_browser_frame_factory_ozone.cc
+++ b/chrome/browser/ui/views/frame/native_browser_frame_factory_ozone.cc
@@ -13,6 +13,6 @@
   if (aura::Env::GetInstance()->mode() == aura::Env::Mode::MUS)
     return new BrowserFrameMus(browser_frame, browser_view);
 
-  NOTREACHED() << "For Ozone builds, only --mash launch is supported for now.";
+  NOTREACHED() << "For Ozone builds, only mash launch is supported for now.";
   return nullptr;
 }
diff --git a/chrome/browser/ui/views/profiles/profile_chooser_view.cc b/chrome/browser/ui/views/profiles/profile_chooser_view.cc
index 1986ae1..8741e14a 100644
--- a/chrome/browser/ui/views/profiles/profile_chooser_view.cc
+++ b/chrome/browser/ui/views/profiles/profile_chooser_view.cc
@@ -117,6 +117,9 @@
 
 constexpr int kVerticalSpacing = 16;
 
+// Number of times the Dice sign-in promo illustration should be shown.
+constexpr int kDiceSigninPromoIllustrationShowCountMax = 10;
+
 bool IsProfileChooser(profiles::BubbleViewMode mode) {
   return mode == profiles::BUBBLE_VIEW_MODE_PROFILE_CHOOSER;
 }
@@ -1032,13 +1035,15 @@
 }
 
 views::View* ProfileChooserView::CreateDiceSigninView() {
+  IncrementDiceSigninPromoShowCount();
   // Fetch signed in GAIA web accounts.
   dice_sync_promo_accounts_ =
       signin_ui_util::GetAccountsForDicePromos(browser_->profile());
 
   // Create a view that holds an illustration, a promo text and a button to turn
-  // on Sync.
-  const int promotext_top_spacing = 24;
+  // on Sync. The promo illustration is only shown the first 10 times per
+  // profile.
+  int promotext_top_spacing = 16;
   const int additional_bottom_spacing =
       dice_sync_promo_accounts_.empty() ? 0 : 8;
   views::View* view = new views::View();
@@ -1046,14 +1051,17 @@
       views::BoxLayout::kVertical,
       gfx::Insets(0, 0, additional_bottom_spacing, 0)));
 
-  // Add the illustration.
-  ui::ResourceBundle& rb = ui::ResourceBundle::GetSharedInstance();
-  views::ImageView* illustration = new views::ImageView();
-  illustration->SetImage(
-      *rb.GetNativeImageNamed(IDR_PROFILES_TURN_ON_SYNC_ILLUSTRATION)
-           .ToImageSkia());
-  view->AddChildView(illustration);
-
+  if (GetDiceSigninPromoShowCount() <=
+      kDiceSigninPromoIllustrationShowCountMax) {
+    // Add the illustration.
+    ui::ResourceBundle& rb = ui::ResourceBundle::GetSharedInstance();
+    views::ImageView* illustration = new views::ImageView();
+    illustration->SetImage(
+        *rb.GetNativeImageNamed(IDR_PROFILES_DICE_TURN_ON_SYNC).ToImageSkia());
+    view->AddChildView(illustration);
+    // Adjust the spacing between illustration and promo text.
+    promotext_top_spacing = 24;
+  }
   // Add the promo text.
   views::Label* promo = new views::Label(
       l10n_util::GetStringUTF16(IDS_PROFILES_DICE_SIGNIN_PROMO));
@@ -1425,3 +1433,13 @@
   else
     ShowViewFromMode(profiles::BUBBLE_VIEW_MODE_GAIA_SIGNIN);
 }
+
+int ProfileChooserView::GetDiceSigninPromoShowCount() const {
+  return browser_->profile()->GetPrefs()->GetInteger(
+      prefs::kDiceSigninUserMenuPromoCount);
+}
+
+void ProfileChooserView::IncrementDiceSigninPromoShowCount() {
+  browser_->profile()->GetPrefs()->SetInteger(
+      prefs::kDiceSigninUserMenuPromoCount, GetDiceSigninPromoShowCount() + 1);
+}
diff --git a/chrome/browser/ui/views/profiles/profile_chooser_view.h b/chrome/browser/ui/views/profiles/profile_chooser_view.h
index f1de764..043bc81e 100644
--- a/chrome/browser/ui/views/profiles/profile_chooser_view.h
+++ b/chrome/browser/ui/views/profiles/profile_chooser_view.h
@@ -181,6 +181,11 @@
   // Callback for DiceAccountsMenu.
   void EnableSync(const base::Optional<AccountInfo>& account);
 
+  // Methods to keep track of the number of times the Dice sign-in promo has
+  // been shown.
+  int GetDiceSigninPromoShowCount() const;
+  void IncrementDiceSigninPromoShowCount();
+
   std::unique_ptr<AvatarMenu> avatar_menu_;
   Browser* const browser_;
 
diff --git a/chrome/browser/ui/views/profiles/profile_chooser_view_browsertest.cc b/chrome/browser/ui/views/profiles/profile_chooser_view_browsertest.cc
index 2ba5e20..713d4b8 100644
--- a/chrome/browser/ui/views/profiles/profile_chooser_view_browsertest.cc
+++ b/chrome/browser/ui/views/profiles/profile_chooser_view_browsertest.cc
@@ -250,6 +250,10 @@
     return ProfileChooserView::profile_bubble_->signin_current_profile_button_;
   }
 
+  int GetDiceSigninPromoShowCount() {
+    return current_profile_bubble()->GetDiceSigninPromoShowCount();
+  }
+
  private:
   std::unique_ptr<content::WindowedNotificationObserver> window_close_observer_;
 
@@ -522,3 +526,25 @@
                        InvokeUi_SupervisedUser) {
   ShowAndVerifyUi();
 }
+
+// Open the profile chooser to increment the Dice sign-in promo show counter
+// below the threshold.
+IN_PROC_BROWSER_TEST_F(ProfileChooserViewExtensionsTest,
+                       IncrementDiceSigninPromoShowCounter) {
+  signin::ScopedAccountConsistencyDice scoped_dice;
+  browser()->profile()->GetPrefs()->SetInteger(
+      prefs::kDiceSigninUserMenuPromoCount, 7);
+  ASSERT_NO_FATAL_FAILURE(OpenProfileChooserView(browser()));
+  EXPECT_EQ(GetDiceSigninPromoShowCount(), 8);
+}
+
+// The DICE sync illustration is shown only the first 10 times. This test
+// ensures that the profile chooser is shown correctly above this threshold.
+IN_PROC_BROWSER_TEST_F(ProfileChooserViewExtensionsTest,
+                       DiceSigninPromoWithoutIllustration) {
+  signin::ScopedAccountConsistencyDice scoped_dice;
+  browser()->profile()->GetPrefs()->SetInteger(
+      prefs::kDiceSigninUserMenuPromoCount, 10);
+  ASSERT_NO_FATAL_FAILURE(OpenProfileChooserView(browser()));
+  EXPECT_EQ(GetDiceSigninPromoShowCount(), 11);
+}
diff --git a/chrome/browser/ui/views/tabs/window_finder_ozone.cc b/chrome/browser/ui/views/tabs/window_finder_ozone.cc
index 15fc4e9..d1be0df5 100644
--- a/chrome/browser/ui/views/tabs/window_finder_ozone.cc
+++ b/chrome/browser/ui/views/tabs/window_finder_ozone.cc
@@ -12,6 +12,6 @@
   if (GetLocalProcessWindowAtPointMus(screen_point, ignore, &mus_result))
     return mus_result;
 
-  NOTREACHED() << "For Ozone builds, only --mash launch is supported for now.";
+  NOTREACHED() << "For Ozone builds, only mash launch is supported for now.";
   return nullptr;
 }
diff --git a/chrome/browser/ui/webui/local_discovery/local_discovery_ui_browsertest.cc b/chrome/browser/ui/webui/local_discovery/local_discovery_ui_browsertest.cc
index fc499de4..3d74af7b 100644
--- a/chrome/browser/ui/webui/local_discovery/local_discovery_ui_browsertest.cc
+++ b/chrome/browser/ui/webui/local_discovery/local_discovery_ui_browsertest.cc
@@ -18,6 +18,8 @@
 #include "base/threading/thread_task_runner_handle.h"
 #include "build/build_config.h"
 #include "chrome/browser/local_discovery/test_service_discovery_client.h"
+#include "chrome/browser/media/router/providers/cast/dual_media_sink_service.h"
+#include "chrome/browser/media/router/test/noop_dual_media_sink_service.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/signin/profile_oauth2_token_service_factory.h"
 #include "chrome/browser/signin/signin_manager_factory.h"
@@ -355,6 +357,16 @@
   ~LocalDiscoveryUITest() override {
   }
 
+  void SetUp() override {
+    // We need to stub out DualMediaSinkService here, because the profile setup
+    // instantiates DualMediaSinkService, which in turn sets
+    // |g_service_discovery_client| with a real instance. This causes
+    // a DCHECK during TestServiceDiscoveryClient construction.
+    media_router::DualMediaSinkService::SetInstanceForTest(
+        new media_router::NoopDualMediaSinkService());
+    WebUIBrowserTest::SetUp();
+  }
+
   void SetUpOnMainThread() override {
     WebUIBrowserTest::SetUpOnMainThread();
 
diff --git a/chrome/browser/ui/webui/media_router/media_router_ui.cc b/chrome/browser/ui/webui/media_router/media_router_ui.cc
index 9a98f0b6..4e10bf5 100644
--- a/chrome/browser/ui/webui/media_router/media_router_ui.cc
+++ b/chrome/browser/ui/webui/media_router/media_router_ui.cc
@@ -343,11 +343,6 @@
   std::unique_ptr<content::WebUIDataSource> html_source(
       content::WebUIDataSource::Create(chrome::kChromeUIMediaRouterHost));
 
-  content::WebContents* wc = web_ui->GetWebContents();
-  DCHECK(wc);
-  content::BrowserContext* context = wc->GetBrowserContext();
-  router_ = MediaRouterFactory::GetApiForBrowserContext(context);
-
   AddLocalizedStrings(html_source.get());
   AddMediaRouterUIResources(html_source.get());
   // Ownership of |html_source| is transferred to the BrowserContext.
@@ -429,11 +424,13 @@
 
 void MediaRouterUI::InitCommon(content::WebContents* initiator) {
   DCHECK(initiator);
-  DCHECK(router_);
 
   TRACE_EVENT_NESTABLE_ASYNC_INSTANT1("media_router", "UI", initiator,
                                       "MediaRouterUI::InitCommon", this);
 
+  router_ = GetMediaRouter();
+  DCHECK(router_);
+
   // Presentation requests from content must show the origin requesting
   // presentation: crbug.com/704964
   if (start_presentation_context_)
@@ -495,7 +492,6 @@
     MediaRouterWebUIMessageHandler* handler,
     std::unique_ptr<StartPresentationContext> context,
     std::unique_ptr<MediaRouterFileDialog> file_dialog) {
-  router_ = router;
   handler_ = handler;
   start_presentation_context_ = std::move(context);
   InitForTest(std::move(file_dialog));
@@ -593,6 +589,13 @@
   TRACE_EVENT_NESTABLE_ASYNC_END0("media_router", "UI", initiator_);
   ui_initialized_ = true;
 
+  // Workaround for MediaRouterElementsBrowserTest, in which MediaRouterUI is
+  // created without calling one of the |Init*()| methods.
+  // TODO(imcheng): We should be able to instantiate |issue_observer_| during
+  // InitCommon by storing an initial Issue in this class.
+  if (!router_)
+    router_ = GetMediaRouter();
+
   // Register for Issue updates.
   issues_observer_ =
       std::make_unique<UIIssuesObserver>(GetIssueManager(), this);
@@ -1125,4 +1128,8 @@
   handler_->UpdateSinks(GetEnabledSinks());
 }
 
+MediaRouter* MediaRouterUI::GetMediaRouter() {
+  return MediaRouterFactory::GetApiForBrowserContext(
+      web_ui()->GetWebContents()->GetBrowserContext());
+}
 }  // namespace media_router
diff --git a/chrome/browser/ui/webui/media_router/media_router_ui.h b/chrome/browser/ui/webui/media_router/media_router_ui.h
index 7bc9d0ee..e8abac5 100644
--- a/chrome/browser/ui/webui/media_router/media_router_ui.h
+++ b/chrome/browser/ui/webui/media_router/media_router_ui.h
@@ -403,6 +403,9 @@
   // Sends the current list of enabled sinks to |handler_|.
   void UpdateSinks();
 
+  // Overridden by tests.
+  virtual MediaRouter* GetMediaRouter();
+
   // Owned by the |web_ui| passed in the ctor, and guaranteed to be deleted
   // only after it has deleted |this|.
   MediaRouterWebUIMessageHandler* handler_ = nullptr;
diff --git a/chrome/browser/ui/webui/media_router/media_router_ui_service_factory_unittest.cc b/chrome/browser/ui/webui/media_router/media_router_ui_service_factory_unittest.cc
index c3d0cbd..3ee9f6d2 100644
--- a/chrome/browser/ui/webui/media_router/media_router_ui_service_factory_unittest.cc
+++ b/chrome/browser/ui/webui/media_router/media_router_ui_service_factory_unittest.cc
@@ -4,6 +4,8 @@
 
 #include <memory>
 
+#include "chrome/browser/media/router/media_router_factory.h"
+#include "chrome/browser/media/router/test/mock_media_router.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/ui/toolbar/toolbar_actions_model.h"
 #include "chrome/browser/ui/toolbar/toolbar_actions_model_factory.h"
@@ -26,6 +28,8 @@
     // requires ToolbarActionsModel.
     builder.AddTestingFactory(ToolbarActionsModelFactory::GetInstance(),
                               BuildFakeToolBarActionsModel);
+    builder.AddTestingFactory(MediaRouterFactory::GetInstance(),
+                              MockMediaRouter::Create);
     profile_ = builder.Build();
   }
 
diff --git a/chrome/browser/ui/webui/media_router/media_router_ui_unittest.cc b/chrome/browser/ui/webui/media_router/media_router_ui_unittest.cc
index 0d4e529..6a6e17ef 100644
--- a/chrome/browser/ui/webui/media_router/media_router_ui_unittest.cc
+++ b/chrome/browser/ui/webui/media_router/media_router_ui_unittest.cc
@@ -117,6 +117,19 @@
   content::PresentationError expected_error_;
 };
 
+class TestMediaRouterUI : public MediaRouterUI {
+ public:
+  TestMediaRouterUI(content::WebUI* web_ui, MediaRouter* router)
+      : MediaRouterUI(web_ui), router_(router) {}
+  ~TestMediaRouterUI() override = default;
+
+  MediaRouter* GetMediaRouter() override { return router_; }
+
+ private:
+  MediaRouter* router_;
+  DISALLOW_COPY_AND_ASSIGN(TestMediaRouterUI);
+};
+
 class MediaRouterUITest : public ChromeRenderViewHostTestHarness {
  public:
   MediaRouterUITest()
@@ -162,7 +175,8 @@
     web_ui_contents_.reset(
         WebContents::Create(WebContents::CreateParams(profile)));
     web_ui_.set_web_contents(web_ui_contents_.get());
-    media_router_ui_ = std::make_unique<MediaRouterUI>(&web_ui_);
+    media_router_ui_ =
+        std::make_unique<TestMediaRouterUI>(&web_ui_, &mock_router_);
     message_handler_ = std::make_unique<MockMediaRouterWebUIMessageHandler>(
         media_router_ui_.get());
 
@@ -213,7 +227,7 @@
   content::TestWebUI web_ui_;
   std::unique_ptr<WebContents> web_ui_contents_;
   std::unique_ptr<StartPresentationContext> start_presentation_context_;
-  std::unique_ptr<MediaRouterUI> media_router_ui_;
+  std::unique_ptr<TestMediaRouterUI> media_router_ui_;
   std::unique_ptr<MockMediaRouterWebUIMessageHandler> message_handler_;
   MockMediaRouterFileDialog* mock_file_dialog_ = nullptr;
   std::vector<MediaSinksObserver*> media_sinks_observers_;
@@ -787,7 +801,8 @@
   web_ui_contents_.reset(
       WebContents::Create(WebContents::CreateParams(profile())));
   web_ui_.set_web_contents(web_ui_contents_.get());
-  media_router_ui_ = std::make_unique<MediaRouterUI>(&web_ui_);
+  media_router_ui_ =
+      std::make_unique<TestMediaRouterUI>(&web_ui_, &mock_router_);
   message_handler_ = std::make_unique<MockMediaRouterWebUIMessageHandler>(
       media_router_ui_.get());
   message_handler_->SetWebUIForTest(&web_ui_);
@@ -797,26 +812,18 @@
         return true;
       }));
   EXPECT_CALL(mock_router_, RegisterMediaRoutesObserver(_)).Times(AnyNumber());
-  // For some reason we push two sets of cast modes to the dialog, even when
-  // initializing the dialog with a presentation request.  The WebUI can handle
-  // the forced mode that is not in the initial cast mode set, but is this a
-  // bug?
-  CastModeSet expected_modes({MediaCastMode::TAB_MIRROR,
-                              MediaCastMode::DESKTOP_MIRROR,
-                              MediaCastMode::LOCAL_FILE});
-  EXPECT_CALL(*message_handler_,
-              UpdateCastModes(
-                  expected_modes, "",
-                  base::Optional<MediaCastMode>(MediaCastMode::PRESENTATION)));
-  expected_modes.insert(MediaCastMode::PRESENTATION);
-  EXPECT_CALL(*message_handler_,
-              UpdateCastModes(
-                  expected_modes, "google.com",
-                  base::Optional<MediaCastMode>(MediaCastMode::PRESENTATION)));
-  media_router_ui_->UIInitialized();
+
+  CastModeSet expected_modes(
+      {MediaCastMode::TAB_MIRROR, MediaCastMode::DESKTOP_MIRROR,
+       MediaCastMode::LOCAL_FILE, MediaCastMode::PRESENTATION});
   media_router_ui_->InitForTest(
       &mock_router_, web_contents(), message_handler_.get(),
       std::move(start_presentation_context_), nullptr);
+  EXPECT_EQ(expected_modes, media_router_ui_->cast_modes());
+  EXPECT_EQ(base::Optional<MediaCastMode>(MediaCastMode::PRESENTATION),
+            media_router_ui_->forced_cast_mode());
+  EXPECT_EQ("google.com", media_router_ui_->GetPresentationRequestSourceName());
+
   // |media_router_ui_| takes ownership of |request_callbacks|.
   media_router_ui_.reset();
 }
diff --git a/chrome/browser/ui/webui/media_router/media_router_web_ui_test.cc b/chrome/browser/ui/webui/media_router/media_router_web_ui_test.cc
index 11ec23b..0b0a38e 100644
--- a/chrome/browser/ui/webui/media_router/media_router_web_ui_test.cc
+++ b/chrome/browser/ui/webui/media_router/media_router_web_ui_test.cc
@@ -4,6 +4,8 @@
 
 #include "chrome/browser/ui/webui/media_router/media_router_web_ui_test.h"
 
+#include "chrome/browser/media/router/media_router_factory.h"
+#include "chrome/browser/media/router/test/mock_media_router.h"
 #include "chrome/browser/ui/toolbar/mock_media_router_action_controller.h"
 #include "chrome/browser/ui/toolbar/toolbar_actions_model.h"
 #include "chrome/browser/ui/toolbar/toolbar_actions_model_factory.h"
@@ -45,14 +47,18 @@
 MediaRouterWebUITest::~MediaRouterWebUITest() {}
 
 TestingProfile::TestingFactories MediaRouterWebUITest::GetTestingFactories() {
+  TestingProfile::TestingFactories factories = {
+      {media_router::MediaRouterFactory::GetInstance(),
+       &media_router::MockMediaRouter::Create}};
   if (require_mock_ui_service_) {
-    return {
-        {media_router::MediaRouterUIServiceFactory::GetInstance(),
-         BuildMockMediaRouterUIService},
-        {ToolbarActionsModelFactory::GetInstance(), BuildToolbarActionsModel}};
+    factories.emplace_back(
+        media_router::MediaRouterUIServiceFactory::GetInstance(),
+        BuildMockMediaRouterUIService);
+    factories.emplace_back(ToolbarActionsModelFactory::GetInstance(),
+                           BuildToolbarActionsModel);
   }
 
-  return BrowserWithTestWindowTest::GetTestingFactories();
+  return factories;
 }
 
 BrowserWindow* MediaRouterWebUITest::CreateBrowserWindow() {
diff --git a/chrome/browser/vr/elements/repositioner.cc b/chrome/browser/vr/elements/repositioner.cc
index 4ce2973..14c47d2 100644
--- a/chrome/browser/vr/elements/repositioner.cc
+++ b/chrome/browser/vr/elements/repositioner.cc
@@ -13,7 +13,32 @@
 
 namespace {
 
-constexpr float kSnapThresholdDegrees = 10.0f;
+constexpr float kHeadUpTransitionStartDegrees = 60.0f;
+constexpr float kHeadUpTransitionEndDegrees = 30.0f;
+constexpr gfx::Vector3dF kUp = {0, 1, 0};
+
+gfx::Vector3dF GetEffectiveUpVector(const gfx::Vector3dF& forward,
+                                    const gfx::Vector3dF& head_forward,
+                                    const gfx::Vector3dF& right,
+                                    const gfx::Vector3dF& head_up) {
+  float degrees_from_up =
+      std::min(gfx::AngleBetweenVectorsInDegrees(forward, kUp),
+               gfx::AngleBetweenVectorsInDegrees(head_forward, kUp));
+  if (degrees_from_up < kHeadUpTransitionEndDegrees)
+    return head_up;
+
+  if (degrees_from_up > kHeadUpTransitionStartDegrees)
+    return kUp;
+
+  gfx::Quaternion q(kUp, head_up);
+  q = gfx::Quaternion().Slerp(
+      q, (kHeadUpTransitionStartDegrees - degrees_from_up) /
+             (kHeadUpTransitionStartDegrees - kHeadUpTransitionEndDegrees));
+
+  gfx::Vector3dF interpolated_up = kUp;
+  gfx::Transform(q).TransformVector(&interpolated_up);
+  return interpolated_up;
+}
 
 }  // namespace
 
@@ -29,43 +54,67 @@
 }
 
 void Repositioner::SetEnabled(bool enabled) {
-  bool check_for_snap = !enabled && enabled_;
   enabled_ = enabled;
-  if (!check_for_snap)
-    return;
-
-  gfx::Vector3dF world_up = {0, 1, 0};
-  gfx::Vector3dF up = world_up;
-  transform_.TransformVector(&up);
-  if (gfx::AngleBetweenVectorsInDegrees(up, world_up) < kSnapThresholdDegrees) {
-    snap_to_world_up_ = true;
+  if (enabled) {
+    initial_transform_ = transform_;
+    initial_laser_direction_ = laser_direction_;
   }
 }
 
+void Repositioner::Reset() {
+  transform_.MakeIdentity();
+}
+
 void Repositioner::UpdateTransform(const gfx::Transform& head_pose) {
-  gfx::Vector3dF head_up_vector =
-      snap_to_world_up_ ? gfx::Vector3dF(0, 1, 0) : vr::GetUpVector(head_pose);
-  snap_to_world_up_ = false;
+  gfx::Vector3dF head_up = vr::GetUpVector(head_pose);
+  gfx::Vector3dF head_forward = vr::GetForwardVector(head_pose);
 
-  transform_.ConcatTransform(
-      gfx::Transform(gfx::Quaternion(last_laser_direction_, laser_direction_)));
+  transform_ = initial_transform_;
+  transform_.ConcatTransform(gfx::Transform(
+      gfx::Quaternion(initial_laser_direction_, laser_direction_)));
 
-  gfx::Vector3dF new_right_vector = {1, 0, 0};
-  transform_.TransformVector(&new_right_vector);
+  gfx::Vector3dF new_right = {1, 0, 0};
+  transform_.TransformVector(&new_right);
 
-  gfx::Vector3dF new_forward_vector = {0, 0, -1};
-  transform_.TransformVector(&new_forward_vector);
+  gfx::Vector3dF forward = {0, 0, -1};
+  gfx::Vector3dF new_forward = forward;
+  transform_.TransformVector(&new_forward);
 
-  gfx::Vector3dF expected_right_vector =
-      gfx::CrossProduct(new_forward_vector, head_up_vector);
-  gfx::Quaternion rotate_to_expected_right(new_right_vector,
-                                           expected_right_vector);
+  new_forward = forward;
+  transform_.TransformVector(&new_forward);
+
+  // Finally we have to correct the roll. I.e., we want to rotate the content
+  // window so that it's oriented "up" and we want to favor world up when we're
+  // near the horizon. GetEffectiveUpVector handles producing the up vector we'd
+  // like to respect.
+  gfx::Vector3dF up =
+      GetEffectiveUpVector(new_forward, head_forward, new_right, head_up);
+
+  gfx::Vector3dF expected_right = gfx::CrossProduct(new_forward, up);
+  gfx::Quaternion rotate_to_expected_right(new_right, expected_right);
   transform_.ConcatTransform(gfx::Transform(rotate_to_expected_right));
+
+  // Potentially bake our current transform, to avoid situations where
+  // |laser_direction_| and |initial_laser_direction_| are nearly 180 degrees
+  // apart causing numeric ambiguity and strange artifacts.
+  //
+  // The reason we do this periodically and not every frame is because of the
+  // pitch and yaw clamping. Effectively, these "throw away" deltas and this can
+  // lead to a situation where the controller moves far past one of the clamp
+  // boundaries (and has no effect), but as soon as it changes direction
+  // (producing deltas in the other direction), the moves do have an effect.
+  // This results in the user's controller being pointed in a very odd direction
+  // to manipulate the window.
+  if (gfx::AngleBetweenVectorsInDegrees(initial_laser_direction_,
+                                        laser_direction_) > 90.0f) {
+    initial_laser_direction_ = laser_direction_;
+    initial_transform_ = transform_;
+  }
 }
 
 bool Repositioner::OnBeginFrame(const base::TimeTicks& time,
                                 const gfx::Transform& head_pose) {
-  if (enabled_ || snap_to_world_up_) {
+  if (enabled_) {
     UpdateTransform(head_pose);
     return true;
   }
diff --git a/chrome/browser/vr/elements/repositioner.h b/chrome/browser/vr/elements/repositioner.h
index 44d9a10ac..832f459 100644
--- a/chrome/browser/vr/elements/repositioner.h
+++ b/chrome/browser/vr/elements/repositioner.h
@@ -19,20 +19,19 @@
 // when enabled as either the head or the controller move. In a nutshell, it
 // rotates the elements per the angular change in the controller orientation,
 // adjusting the up vector of the content so that it aligns with the head's up
-// vector. If, after adjusting the transform, the computed up vector is within a
-// 10 degree threshold of true, world up, then we snap the up vector (to avoid
-// having the window slightly skewed with respect to the horizon).
+// vector. As the window is being repositioned, we rotate it so that it remains
+// pointing upward.
 class Repositioner : public UiElement {
  public:
   Repositioner();
   ~Repositioner() override;
 
   void set_laser_direction(const gfx::Vector3dF& laser_direction) {
-    last_laser_direction_ = laser_direction_;
     laser_direction_ = laser_direction;
   }
 
   void SetEnabled(bool enabled);
+  void Reset();
 
  private:
   gfx::Transform LocalTransform() const override;
@@ -44,11 +43,12 @@
   void DumpGeometry(std::ostringstream* os) const override;
 #endif
 
-  gfx::Transform transform_;
   bool enabled_ = false;
+  gfx::Transform transform_;
   gfx::Vector3dF laser_direction_;
-  gfx::Vector3dF last_laser_direction_;
-  bool snap_to_world_up_ = false;
+
+  gfx::Transform initial_transform_;
+  gfx::Vector3dF initial_laser_direction_;
 
   DISALLOW_COPY_AND_ASSIGN(Repositioner);
 };
diff --git a/chrome/browser/vr/elements/repositioner_unittest.cc b/chrome/browser/vr/elements/repositioner_unittest.cc
index 034634d..69fb340 100644
--- a/chrome/browser/vr/elements/repositioner_unittest.cc
+++ b/chrome/browser/vr/elements/repositioner_unittest.cc
@@ -73,8 +73,11 @@
       {{1, 0, 0}, {0, 1, 0}, {kTestContentDistance, 0, 0}, {0, 0, 1}},
       // Should snap as head position is within threshold of up.
       {{0, 0, -1}, {0, 0.9f, 0}, {0, 0, -kTestContentDistance}, {1, 0, 0}},
-      // Should not snap, viewer is lying on their side.
-      {{0, 0, -1}, {1, 0, 0}, {0, 0, -kTestContentDistance}, {0, 1, 0}},
+      // If the user's head is leaning far back, we do tilt.
+      {{0, 1, 0},
+       {0.1f, 0.1f, 0.8f},
+       {0, kTestContentDistance, 0},
+       {-0.992278f, 0, 0.124035f}},
   };
 
   for (size_t i = 0; i < test_cases.size(); i++) {
diff --git a/chrome/browser/vr/model/controller_model.h b/chrome/browser/vr/model/controller_model.h
index 6b1e54f..9f99940 100644
--- a/chrome/browser/vr/model/controller_model.h
+++ b/chrome/browser/vr/model/controller_model.h
@@ -30,6 +30,7 @@
   float opacity = 1.0f;
   bool quiescent = false;
   bool resting_in_viewport = false;
+  bool recentered = false;
   PlatformController::Handedness handedness = PlatformController::kRightHanded;
 };
 
diff --git a/chrome/browser/vr/platform_controller.h b/chrome/browser/vr/platform_controller.h
index 83b15ec8..d24da88 100644
--- a/chrome/browser/vr/platform_controller.h
+++ b/chrome/browser/vr/platform_controller.h
@@ -43,6 +43,7 @@
   virtual base::TimeTicks GetLastTouchTimestamp() const = 0;
   virtual base::TimeTicks GetLastButtonTimestamp() const = 0;
   virtual Handedness GetHandedness() const = 0;
+  virtual bool GetRecentered() const = 0;
 };
 
 }  // namespace vr
diff --git a/chrome/browser/vr/ui_scene_creator.cc b/chrome/browser/vr/ui_scene_creator.cc
index c6e16d0..6ec3a83 100644
--- a/chrome/browser/vr/ui_scene_creator.cc
+++ b/chrome/browser/vr/ui_scene_creator.cc
@@ -657,6 +657,9 @@
   repositioner->AddBinding(VR_BIND_FUNC(
       gfx::Vector3dF, Model, model_, model->controller.laser_direction,
       Repositioner, repositioner.get(), set_laser_direction));
+  repositioner->AddBinding(
+      VR_BIND(bool, Model, model_, model->controller.recentered, Repositioner,
+              repositioner.get(), if (value) { view->Reset(); }));
   scene_->AddUiElement(k2dBrowsingRoot, std::move(repositioner));
 
   element = Create<UiElement>(k2dBrowsingVisibiltyControlForVoice, kPhaseNone);
@@ -842,6 +845,7 @@
         if (focused) {
           e->UpdateInput(model->web_input_text_field_info);
         } else {
+          model->editing_web_input = false;
           e->UpdateInput(EditedText());
         }
       },
diff --git a/chrome/browser/vr/ui_unittest.cc b/chrome/browser/vr/ui_unittest.cc
index df185be..c865121 100644
--- a/chrome/browser/vr/ui_unittest.cc
+++ b/chrome/browser/vr/ui_unittest.cc
@@ -14,6 +14,7 @@
 #include "chrome/browser/vr/elements/disc_button.h"
 #include "chrome/browser/vr/elements/exit_prompt.h"
 #include "chrome/browser/vr/elements/rect.h"
+#include "chrome/browser/vr/elements/repositioner.h"
 #include "chrome/browser/vr/elements/ui_element.h"
 #include "chrome/browser/vr/elements/ui_element_name.h"
 #include "chrome/browser/vr/elements/vector_icon.h"
@@ -1175,6 +1176,21 @@
   EXPECT_EQ(kRepositionButtonMinOpacity, button->GetTargetOpacity());
 }
 
+TEST_F(UiTest, ResetRepositioner) {
+  CreateScene(kNotInCct, kNotInWebVr);
+  Repositioner* repositioner = static_cast<Repositioner*>(
+      scene_->GetUiElementByName(k2dBrowsingRepositioner));
+  repositioner->set_laser_direction(kForwardVector);
+  repositioner->SetEnabled(true);
+  repositioner->set_laser_direction({0, 1, 0});
+  OnBeginFrame();
+  EXPECT_FALSE(repositioner->world_space_transform().IsIdentity());
+  repositioner->SetEnabled(false);
+  model_->controller.recentered = true;
+  OnBeginFrame();
+  EXPECT_TRUE(repositioner->world_space_transform().IsIdentity());
+}
+
 // No element in the controller root's subtree should be hit testable.
 TEST_F(UiTest, ControllerHitTest) {
   CreateScene(kNotInCct, kNotInWebVr);
diff --git a/chrome/common/chrome_features.cc b/chrome/common/chrome_features.cc
index 39b5331..3d39647f 100644
--- a/chrome/common/chrome_features.cc
+++ b/chrome/common/chrome_features.cc
@@ -221,6 +221,16 @@
                                              base::FEATURE_DISABLED_BY_DEFAULT};
 #endif
 
+#if defined(OS_CHROMEOS)
+// If enabled, the Chrome OS Settings UI will include a menu for the unified
+// MultiDevice settings.
+const base::Feature kEnableUnifiedMultiDeviceSettings{
+    "EnableUnifiedMultiDeviceSettings", base::FEATURE_DISABLED_BY_DEFAULT};
+// Enable the device to setup all MultiDevice services in a single workflow.
+const base::Feature kEnableUnifiedMultiDeviceSetup{
+    "EnableUnifiedMultiDeviceSetup", base::FEATURE_DISABLED_BY_DEFAULT};
+#endif
+
 // Enables Expect CT reporting, which sends reports for opted-in sites
 // that don't serve sufficient Certificate Transparency information.
 const base::Feature kExpectCTReporting{"ExpectCTReporting",
diff --git a/chrome/common/chrome_features.h b/chrome/common/chrome_features.h
index 76e5242..8cbecdd 100644
--- a/chrome/common/chrome_features.h
+++ b/chrome/common/chrome_features.h
@@ -120,6 +120,11 @@
 extern const base::Feature kDownloadsLocationChange;
 #endif
 
+#if defined(OS_CHROMEOS)
+extern const base::Feature kEnableUnifiedMultiDeviceSettings;
+extern const base::Feature kEnableUnifiedMultiDeviceSetup;
+#endif
+
 extern const base::Feature kExpectCTReporting;
 
 extern const base::Feature kExperimentalAppBanners;
diff --git a/chrome/common/chrome_switches.cc b/chrome/common/chrome_switches.cc
index 4f14cbf..90e46ab 100644
--- a/chrome/common/chrome_switches.cc
+++ b/chrome/common/chrome_switches.cc
@@ -812,9 +812,6 @@
 // installed through policy.
 const char kDisableLoginScreenApps[] = "disable-login-screen-apps";
 
-// Enables out-of-process ash and mus (ui service). See //ash/README.md
-const char kMash[] = "mash";
-
 // Provides the name of the mojo service running in a mash utility process.
 // NOTE: Used by the Chrome OS crash_reporter to identify mash processes. If you
 // change or remove the flag please update platform2/crash_reporter.
diff --git a/chrome/common/chrome_switches.h b/chrome/common/chrome_switches.h
index 23b0519..b19289d 100644
--- a/chrome/common/chrome_switches.h
+++ b/chrome/common/chrome_switches.h
@@ -254,7 +254,6 @@
 extern const char kCroshCommand[];
 extern const char kDisableLoggingRedirect[];
 extern const char kDisableLoginScreenApps[];
-extern const char kMash[];
 extern const char kMashServiceName[];
 #endif  // defined(OS_CHROMEOS)
 
diff --git a/chrome/common/pref_names.cc b/chrome/common/pref_names.cc
index ceb04ee..d5be002 100644
--- a/chrome/common/pref_names.cc
+++ b/chrome/common/pref_names.cc
@@ -1549,6 +1549,10 @@
 const char kGoogleServicesPasswordHash[] = "google.services.password_hash";
 
 #if !defined(OS_ANDROID)
+// Tracks the number of times the dice signin promo has been shown in the user
+// menu.
+const char kDiceSigninUserMenuPromoCount[] = "sync_promo.user_menu_show_count";
+
 // Tracks the number of times that we have shown the sign in promo at startup.
 const char kSignInPromoStartupCount[] = "sync_promo.startup_count";
 
diff --git a/chrome/common/pref_names.h b/chrome/common/pref_names.h
index 705e971d..7c3323d 100644
--- a/chrome/common/pref_names.h
+++ b/chrome/common/pref_names.h
@@ -556,6 +556,7 @@
 extern const char kGoogleServicesPasswordHash[];
 
 #if !defined(OS_ANDROID)
+extern const char kDiceSigninUserMenuPromoCount[];
 extern const char kSignInPromoStartupCount[];
 extern const char kSignInPromoUserSkipped[];
 extern const char kSignInPromoShowOnFirstRunAllowed[];
diff --git a/chrome/renderer/prerender/OWNERS b/chrome/renderer/prerender/OWNERS
index 94c5922..e0db6795 100644
--- a/chrome/renderer/prerender/OWNERS
+++ b/chrome/renderer/prerender/OWNERS
@@ -1,5 +1,3 @@
-cbentzel@chromium.org
-gavinp@chromium.org
 mmenke@chromium.org
 pasko@chromium.org
 
diff --git a/chrome/service/BUILD.gn b/chrome/service/BUILD.gn
index b77d425..058473db 100644
--- a/chrome/service/BUILD.gn
+++ b/chrome/service/BUILD.gn
@@ -4,6 +4,7 @@
 
 import("//build/config/features.gni")
 import("//printing/features/features.gni")
+import("//services/catalog/public/tools/catalog.gni")
 
 assert(!is_chromeos)
 
@@ -54,6 +55,7 @@
   configs += [ "//build/config/compiler:wexit_time_destructors" ]
 
   deps = [
+    ":service_process_catalog_source",
     "//base",
     "//chrome:strings",
     "//chrome/common",
@@ -84,3 +86,15 @@
     ]
   }
 }
+
+catalog("service_process_catalog") {
+  embedded_services = [
+    "//content/public/app:browser_manifest",
+    "//content/public/app:utility_manifest",
+  ]
+}
+
+catalog_cpp_source("service_process_catalog_source") {
+  catalog = ":service_process_catalog"
+  generated_function_name = "CreateServiceProcessCatalog"
+}
diff --git a/chrome/service/DEPS b/chrome/service/DEPS
index 893a993f..ff020e2c 100644
--- a/chrome/service/DEPS
+++ b/chrome/service/DEPS
@@ -8,6 +8,5 @@
   "+components/version_info",
   "+mojo/edk/embedder",
   "+sandbox/win/src",
-  "+services/service_manager/public",
-  "+services/service_manager/sandbox",
+  "+services/service_manager",
 ]
diff --git a/chrome/service/service_utility_process_host.cc b/chrome/service/service_utility_process_host.cc
index 10234a5..ee3c367 100644
--- a/chrome/service/service_utility_process_host.cc
+++ b/chrome/service/service_utility_process_host.cc
@@ -21,6 +21,7 @@
 #include "base/macros.h"
 #include "base/metrics/histogram_macros.h"
 #include "base/process/launch.h"
+#include "base/process/process_handle.h"
 #include "base/single_thread_task_runner.h"
 #include "base/strings/string_number_conversions.h"
 #include "base/task_runner_util.h"
@@ -28,13 +29,17 @@
 #include "base/win/win_util.h"
 #include "chrome/common/chrome_switches.h"
 #include "chrome/common/chrome_utility_printing_messages.h"
+#include "chrome/service/service_process_catalog_source.h"
 #include "chrome/services/printing/public/mojom/pdf_to_emf_converter.mojom.h"
 #include "content/public/common/child_process_host.h"
+#include "content/public/common/connection_filter.h"
 #include "content/public/common/content_switches.h"
+#include "content/public/common/font_cache_dispatcher_win.h"
 #include "content/public/common/mojo_channel_switches.h"
 #include "content/public/common/result_codes.h"
 #include "content/public/common/sandbox_init.h"
 #include "content/public/common/sandboxed_process_launcher_delegate.h"
+#include "content/public/common/service_manager_connection.h"
 #include "content/public/common/service_names.mojom.h"
 #include "mojo/edk/embedder/embedder.h"
 #include "mojo/edk/embedder/named_platform_channel_pair.h"
@@ -45,7 +50,14 @@
 #include "printing/emf_win.h"
 #include "sandbox/win/src/sandbox_policy.h"
 #include "sandbox/win/src/sandbox_types.h"
+#include "services/service_manager/public/cpp/binder_registry.h"
+#include "services/service_manager/public/cpp/connector.h"
+#include "services/service_manager/public/mojom/constants.mojom.h"
+#include "services/service_manager/public/mojom/service.mojom.h"
+#include "services/service_manager/runner/host/service_process_launcher.h"
+#include "services/service_manager/runner/host/service_process_launcher_factory.h"
 #include "services/service_manager/sandbox/sandbox_type.h"
+#include "services/service_manager/service_manager.h"
 #include "ui/base/ui_base_switches.h"
 
 namespace {
@@ -117,6 +129,49 @@
   mojo::Binding<printing::mojom::PdfToEmfConverterClient> binding_;
 };
 
+class NullServiceProcessLauncherFactory
+    : public service_manager::ServiceProcessLauncherFactory {
+ public:
+  NullServiceProcessLauncherFactory() = default;
+  ~NullServiceProcessLauncherFactory() override = default;
+
+  // service_manager::ServiceProcessLauncherFactory:
+  std::unique_ptr<service_manager::ServiceProcessLauncher> Create(
+      const base::FilePath& service_path) override {
+    LOG(ERROR) << "Attempting to run unsupported native service: "
+               << service_path.value();
+    return nullptr;
+  }
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(NullServiceProcessLauncherFactory);
+};
+
+class ConnectionFilterImpl : public content::ConnectionFilter {
+ public:
+  ConnectionFilterImpl() {
+    registry_.AddInterface(
+        base::BindRepeating(&content::FontCacheDispatcher::Create));
+  }
+
+  ~ConnectionFilterImpl() override = default;
+
+  // content::ConnectionFilter:
+  void OnBindInterface(const service_manager::BindSourceInfo& source_info,
+                       const std::string& interface_name,
+                       mojo::ScopedMessagePipeHandle* interface_pipe,
+                       service_manager::Connector* connector) override {
+    registry_.TryBindInterface(interface_name, interface_pipe, source_info);
+  }
+
+ private:
+  service_manager::BinderRegistryWithArgs<
+      const service_manager::BindSourceInfo&>
+      registry_;
+
+  DISALLOW_COPY_AND_ASSIGN(ConnectionFilterImpl);
+};
+
 }  // namespace
 
 class ServiceUtilityProcessHost::PdfToEmfState {
@@ -304,9 +359,52 @@
     return false;
   }
 
+  // We set up a Service Manager for each utility process hosted here. The
+  // utility processes launched by the service process only ever need to
+  // communicate back to the service process itself, so it's safe to host each
+  // one using its own isolated service manager.
+  //
+  // We do this because some common child process code expects to be connected
+  // to a well-behaved service manager from which it can request common host
+  // interfaces.
+  //
+  // In the isolated service environment there are exactly two service
+  // instances: this process, which masquerades as "content_browser"; and the
+  // child process, which exists ostensibly as the only instance of
+  // "content_utility". This is all set up here.
+  service_manager_ = std::make_unique<service_manager::ServiceManager>(
+      std::make_unique<NullServiceProcessLauncherFactory>(),
+      CreateServiceProcessCatalog(), nullptr);
+
+  service_manager::mojom::ServicePtr browser_proxy;
+  service_manager_connection_ = content::ServiceManagerConnection::Create(
+      mojo::MakeRequest(&browser_proxy),
+      base::SequencedTaskRunnerHandle::Get());
+  service_manager_connection_->AddConnectionFilter(
+      std::make_unique<ConnectionFilterImpl>());
+
+  service_manager::mojom::PIDReceiverPtr pid_receiver;
+  service_manager_->RegisterService(
+      service_manager::Identity(content::mojom::kBrowserServiceName,
+                                service_manager::mojom::kRootUserID),
+      std::move(browser_proxy), mojo::MakeRequest(&pid_receiver));
+  pid_receiver->SetPID(base::GetCurrentProcId());
+  pid_receiver.reset();
+
   std::string mojo_bootstrap_token = mojo::edk::GenerateRandomToken();
-  utility_process_connection_.Bind(service_manager::mojom::ServicePtrInfo(
+  service_manager::mojom::ServicePtr utility_service;
+  utility_service.Bind(service_manager::mojom::ServicePtrInfo(
       broker_client_invitation_.AttachMessagePipe(mojo_bootstrap_token), 0u));
+  service_manager_->RegisterService(
+      service_manager::Identity(content::mojom::kUtilityServiceName,
+                                service_manager::mojom::kRootUserID),
+      std::move(utility_service), mojo::MakeRequest(&pid_receiver));
+  pid_receiver->SetPID(base::GetCurrentProcId());
+
+  service_manager_connection_->Start();
+
+  // NOTE: This call to |CreateChannelMojo()| requires a working
+  // ServiceManagerConnection to have already been established.
   child_process_host_->CreateChannelMojo();
 
   base::CommandLine cmd_line(exe_path);
@@ -429,14 +527,9 @@
 void ServiceUtilityProcessHost::BindInterface(
     const std::string& interface_name,
     mojo::ScopedMessagePipeHandle interface_pipe) {
-  service_manager::BindSourceInfo source_info;
-  // ChildThreadImpl expects a connection from the browser process for
-  // establishing its legacy IPC channel.
-  source_info.identity =
-      service_manager::Identity{content::mojom::kBrowserServiceName};
-  utility_process_connection_->OnBindInterface(source_info, interface_name,
-                                               std::move(interface_pipe),
-                                               base::Bind(&base::DoNothing));
+  service_manager_connection_->GetConnector()->BindInterface(
+      service_manager::Identity(content::mojom::kUtilityServiceName),
+      interface_name, std::move(interface_pipe));
 }
 
 void ServiceUtilityProcessHost::OnMetafileSpooled(bool success) {
diff --git a/chrome/service/service_utility_process_host.h b/chrome/service/service_utility_process_host.h
index 2379c9d..ca5ff62b 100644
--- a/chrome/service/service_utility_process_host.h
+++ b/chrome/service/service_utility_process_host.h
@@ -15,7 +15,6 @@
 #include "content/public/common/child_process_host_delegate.h"
 #include "ipc/ipc_platform_file.h"
 #include "mojo/edk/embedder/outgoing_broker_client_invitation.h"
-#include "services/service_manager/public/mojom/service.mojom.h"
 
 namespace base {
 class CommandLine;
@@ -26,6 +25,7 @@
 
 namespace content {
 class ChildProcessHost;
+class ServiceManagerConnection;
 }
 
 namespace printing {
@@ -35,6 +35,10 @@
 struct PrinterSemanticCapsAndDefaults;
 }  // namespace printing
 
+namespace service_manager {
+class ServiceManager;
+}
+
 // Acts as the service-side host to a utility child process. A
 // utility process is a short-lived sandboxed process that is created to run
 // a specific task.
@@ -161,7 +165,9 @@
   class PdfToEmfState;
   std::unique_ptr<PdfToEmfState> pdf_to_emf_state_;
 
-  service_manager::mojom::ServicePtr utility_process_connection_;
+  std::unique_ptr<service_manager::ServiceManager> service_manager_;
+  std::unique_ptr<content::ServiceManagerConnection>
+      service_manager_connection_;
 
   base::WeakPtrFactory<ServiceUtilityProcessHost> weak_ptr_factory_;
 
diff --git a/chrome/test/BUILD.gn b/chrome/test/BUILD.gn
index 099c57d4..3b402db 100644
--- a/chrome/test/BUILD.gn
+++ b/chrome/test/BUILD.gn
@@ -324,11 +324,7 @@
   ]
 
   if (is_chromeos) {
-    sources += [
-      "base/browser_tests_main_chromeos.cc",
-      "base/mash_browser_tests_main.cc",
-      "base/mash_browser_tests_main.h",
-    ]
+    sources += [ "base/browser_tests_main_chromeos.cc" ]
   } else {
     sources += [ "base/browser_tests_main.cc" ]
   }
@@ -794,7 +790,7 @@
       "../browser/ui/search/local_ntp_test_utils.cc",
       "../browser/ui/search/local_ntp_test_utils.h",
       "../browser/ui/search/local_ntp_voice_search_browsertest.cc",
-      "../browser/ui/search/new_tab_page_navigation_throttle_browsertest.cc",
+      "../browser/ui/search/new_tab_page_interceptor_browsertest.cc",
       "../browser/ui/search_engines/search_engine_tab_helper_browsertest.cc",
       "../browser/ui/settings_window_manager_browsertest_chromeos.cc",
       "../browser/ui/startup/startup_browser_creator_browsertest.cc",
@@ -1370,6 +1366,7 @@
         "../browser/ui/views/extensions/extension_message_bubble_view_browsertest.cc",
         "../browser/ui/views/extensions/pwa_confirmation_view_browsertest.cc",
         "../browser/ui/views/external_protocol_dialog_browsertest.cc",
+        "../browser/ui/views/folder_upload_confirmation_view_browsertest.cc",
         "../browser/ui/views/frame/browser_non_client_frame_view_browsertest_win.cc",
         "../browser/ui/views/frame/browser_window_property_manager_browsertest_win.cc",
         "../browser/ui/views/importer/import_lock_dialog_view_browsertest.cc",
@@ -2043,6 +2040,11 @@
     if (!is_android && !is_mac) {
       data_deps += [ "//chrome/browser/resources/media/mei_preload:component" ]
     }
+
+    if (toolkit_views && !is_chromeos) {
+      sources +=
+          [ "../browser/ui/screen_capture_notification_ui_browsertest.cc" ]
+    }
   }
 }
 
diff --git a/chrome/test/base/browser_tests_main_chromeos.cc b/chrome/test/base/browser_tests_main_chromeos.cc
index 57f7776..9e7cae0 100644
--- a/chrome/test/base/browser_tests_main_chromeos.cc
+++ b/chrome/test/base/browser_tests_main_chromeos.cc
@@ -6,13 +6,8 @@
 #include "base/test/launcher/test_launcher.h"
 #include "chrome/test/base/chrome_test_launcher.h"
 #include "chrome/test/base/chrome_test_suite.h"
-#include "chrome/test/base/mash_browser_tests_main.h"
 
 int main(int argc, char** argv) {
-  int exit_code = 0;
-  if (RunMashBrowserTests(argc, argv, &exit_code))
-    return exit_code;
-
   base::CommandLine::Init(argc, argv);
   size_t parallel_jobs = base::NumParallelJobs();
   if (parallel_jobs == 0U) {
diff --git a/chrome/test/base/chrome_test_launcher.cc b/chrome/test/base/chrome_test_launcher.cc
index 337edb7..9cd4368 100644
--- a/chrome/test/base/chrome_test_launcher.cc
+++ b/chrome/test/base/chrome_test_launcher.cc
@@ -63,12 +63,6 @@
 ChromeTestSuiteRunner::~ChromeTestSuiteRunner() {}
 
 int ChromeTestSuiteRunner::RunTestSuite(int argc, char** argv) {
-#if defined(USE_AURA)
-  base::CommandLine* command_line = base::CommandLine::ForCurrentProcess();
-  if (command_line->HasSwitch(service_manager::switches::kServicePipeToken))
-    content::GetContentMainParams()->env_mode = aura::Env::Mode::MUS;
-#endif  // defined(USE_AURA)
-
   return ChromeTestSuite(argc, argv).Run();
 }
 
diff --git a/chrome/test/base/mash_browser_tests_main.cc b/chrome/test/base/mash_browser_tests_main.cc
deleted file mode 100644
index 4651dbe..0000000
--- a/chrome/test/base/mash_browser_tests_main.cc
+++ /dev/null
@@ -1,90 +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.
-
-#include "base/command_line.h"
-#include "chrome/common/chrome_switches.h"
-#include "chrome/test/base/chrome_test_launcher.h"
-#include "chrome/test/base/chrome_test_suite.h"
-#include "content/public/app/content_main.h"
-#include "content/public/common/content_switches.h"
-#include "content/public/common/service_names.mojom.h"
-#include "content/public/test/test_launcher.h"
-#include "ui/aura/env.h"
-#include "ui/base/ui_base_switches.h"
-
-namespace {
-
-// Enumeration of the possible chrome-ash configurations.
-enum class AshConfig {
-  // Aura is backed by mus, but chrome and ash are still in the same process.
-  MUS,
-
-  // Aura is backed by mus and chrome and ash are in separate processes. In
-  // this mode chrome code can only use ash code in ash/public/cpp.
-  MASH,
-};
-
-class MusTestLauncherDelegate : public ChromeTestLauncherDelegate {
- public:
-  explicit MusTestLauncherDelegate(ChromeTestSuiteRunner* runner)
-      : ChromeTestLauncherDelegate(runner),
-        config_(
-            base::CommandLine::ForCurrentProcess()->HasSwitch(switches::kMash)
-                ? AshConfig::MASH
-                : AshConfig::MUS) {}
-
-  ~MusTestLauncherDelegate() override {}
-
- private:
-  // ChromeTestLauncherDelegate:
-  int RunTestSuite(int argc, char** argv) override {
-    content::GetContentMainParams()->env_mode = aura::Env::Mode::MUS;
-    content::GetContentMainParams()->create_discardable_memory =
-        (config_ == AshConfig::MUS);
-    if (config_ == AshConfig::MASH) {
-      base::CommandLine::ForCurrentProcess()->AppendSwitch(switches::kMus);
-      base::CommandLine::ForCurrentProcess()->AppendSwitch(
-          switches::kMusHostingViz);
-    }
-    return ChromeTestLauncherDelegate::RunTestSuite(argc, argv);
-  }
-
-  AshConfig config_;
-
-  DISALLOW_COPY_AND_ASSIGN(MusTestLauncherDelegate);
-};
-
-}  // namespace
-
-bool RunMashBrowserTests(int argc, char** argv, int* exit_code) {
-  base::CommandLine::Init(argc, argv);
-  base::CommandLine* command_line = base::CommandLine::ForCurrentProcess();
-  if (!command_line->HasSwitch(switches::kMash) &&
-      !command_line->HasSwitch(switches::kMus)) {
-    // Currently launching content_package_services via the browser_tests binary
-    // will lead to a nested test suite, trying to run all tests again. However
-    // they will be in a strange mixed mode, of a local-Ash, but non-local Aura.
-    //
-    // This leads to continuous crashes in OzonePlatform.
-    //
-    // For now disable this launch until the requesting site can be identified.
-    //
-    // TODO(jonross): find an appropriate way to launch content_package_services
-    // within the mash_browser_tests (crbug.com/738449)
-    if (command_line->GetSwitchValueASCII("service-name") ==
-        content::mojom::kPackagedServicesServiceName) {
-      return true;
-    }
-    return false;
-  }
-
-  size_t parallel_jobs = base::NumParallelJobs();
-  ChromeTestSuiteRunner chrome_test_suite_runner;
-  MusTestLauncherDelegate test_launcher_delegate(&chrome_test_suite_runner);
-  if (command_line->HasSwitch(switches::kMash) && parallel_jobs > 1U)
-    parallel_jobs /= 2U;
-  *exit_code =
-      LaunchChromeTests(parallel_jobs, &test_launcher_delegate, argc, argv);
-  return true;
-}
diff --git a/chrome/test/base/mash_browser_tests_main.h b/chrome/test/base/mash_browser_tests_main.h
deleted file mode 100644
index 39583eda..0000000
--- a/chrome/test/base/mash_browser_tests_main.h
+++ /dev/null
@@ -1,12 +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.
-
-#ifndef CHROME_TEST_BASE_MASH_BROWSER_TESTS_MAIN_H_
-#define CHROME_TEST_BASE_MASH_BROWSER_TESTS_MAIN_H_
-
-// Return true if mash browser tests were run, false otherwise. If the tests
-// were run |exit_code| is set appropriately.
-bool RunMashBrowserTests(int argc, char** argv, int* exit_code);
-
-#endif  // CHROME_TEST_BASE_MASH_BROWSER_TESTS_MAIN_H_
diff --git a/chrome/test/chromedriver/client/command_executor.py b/chrome/test/chromedriver/client/command_executor.py
index 8f58f15..dca48d4 100644
--- a/chrome/test/chromedriver/client/command_executor.py
+++ b/chrome/test/chromedriver/client/command_executor.py
@@ -4,6 +4,9 @@
 
 import httplib
 import json
+import socket
+import subprocess
+import sys
 
 
 class _Method(object):
@@ -189,8 +192,13 @@
     body = None
     if command[0] == _Method.POST:
       body = json.dumps(params)
-    self._http_client.request(command[0], '/'.join(substituted_parts), body)
-    response = self._http_client.getresponse()
+    try:
+      self._http_client.request(command[0], '/'.join(substituted_parts), body)
+      response = self._http_client.getresponse()
+    except socket.timeout:
+      if sys.platform == 'linux2' or sys.platform == 'darwin':
+        subprocess.call(['ps', 'alx'])
+      raise
 
     if response.status == 303:
       self._http_client.request(_Method.GET, response.getheader('location'))
diff --git a/chrome/test/data/autofill/label_change.html b/chrome/test/data/autofill/label_change.html
new file mode 100644
index 0000000..0e037102
--- /dev/null
+++ b/chrome/test/data/autofill/label_change.html
@@ -0,0 +1,28 @@
+<!DOCTYPE html>
+<html>
+<head>
+</head>
+<body>
+<script>
+
+function delayedUpload() {
+  window.domAutomationController.send("SUBMISSION_FINISHED");
+  console.log("delayedUpload")
+}
+
+function send_post() {
+  setTimeout(delayedUpload, 0);
+}
+
+</script>
+
+<form action="" onsubmit="send_post(); return false;">
+<label id="label_id"> Address </label>
+<input type="text" id="address" name="address" autocomplete="on">
+
+<p id="p_id">Address 1</p>
+<input type="text" name="address1" autocomplete="on">
+<input type="submit" id="submit_button" name="submit_button">
+</form>
+</body>
+</html>
diff --git a/content/test/data/browsing_data/empty_worker.js b/chrome/test/data/browsing_data/empty_worker.js
similarity index 100%
rename from content/test/data/browsing_data/empty_worker.js
rename to chrome/test/data/browsing_data/empty_worker.js
diff --git a/content/test/data/browsing_data/site_data.html b/chrome/test/data/browsing_data/site_data.html
similarity index 100%
rename from content/test/data/browsing_data/site_data.html
rename to chrome/test/data/browsing_data/site_data.html
diff --git a/chrome/test/data/extensions/api_test/content_scripts/ntp/background.js b/chrome/test/data/extensions/api_test/content_scripts/ntp/background.js
index d31eb37..b1f8ea2 100644
--- a/chrome/test/data/extensions/api_test/content_scripts/ntp/background.js
+++ b/chrome/test/data/extensions/api_test/content_scripts/ntp/background.js
@@ -2,20 +2,12 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-var newTabUrls = [
-  'chrome://newtab/',
-  // The tab URL will be redirected to the Local New Tab Page if
-  // features::kUseGoogleLocalNtp is not enabled.
-  'chrome-search://local-ntp/local-ntp.html',
-];
-
 function testExecuteScriptInNewTab() {
   // Create a new tab to chrome://newtab and wait for the loading to complete.
   // Then, try to inject a script into that tab. The injection should fail.
   chrome.tabs.onUpdated.addListener(function listener(tabId, changeInfo, tab) {
-    if (!newTabUrls.includes(tab.url) || changeInfo.status != 'complete') {
+    if (tab.url != 'chrome://newtab/' || changeInfo.status != 'complete')
       return;
-    }
     chrome.tabs.onUpdated.removeListener(listener);
     chrome.tabs.executeScript(tab.id, {file: 'script.js'}, function() {
       chrome.test.assertTrue(!!chrome.runtime.lastError);
diff --git a/chrome/test/data/extensions/api_test/tabs/basics/crud2.js b/chrome/test/data/extensions/api_test/tabs/basics/crud2.js
index d0f37879..09fa0cb 100644
--- a/chrome/test/data/extensions/api_test/tabs/basics/crud2.js
+++ b/chrome/test/data/extensions/api_test/tabs/basics/crud2.js
@@ -2,13 +2,6 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-var newTabUrls = [
-  'chrome://newtab/',
-  // The tab URL will be redirected to the Local New Tab Page if
-  // features::kUseGoogleLocalNtp is not enabled.
-  'chrome-search://local-ntp/local-ntp.html',
-];
-
 var secondWindowId;
 var thirdWindowId;
 var testTabId;
@@ -46,7 +39,7 @@
         assertEq((i == 0), tabs[i].active && tabs[i].selected);
       }
       assertEq("about:blank", tabs[0].url);
-      assertTrue(newTabUrls.includes(tabs[1].url));
+      assertEq("chrome://newtab/", tabs[1].url);
       assertEq(pageUrl("a"), tabs[2].url);
     }));
 
@@ -57,7 +50,7 @@
         assertEq(thirdWindowId, tabs[i].windowId);
         assertEq(i, tabs[i].index);
       }
-      assertTrue(newTabUrls.includes(tabs[0].url));
+      assertEq("chrome://newtab/", tabs[0].url);
       assertEq(pageUrl("b"), tabs[1].url);
     }));
   },
diff --git a/chrome/test/data/extensions/api_test/tabs/basics/move.js b/chrome/test/data/extensions/api_test/tabs/basics/move.js
index e64c15f21..aefe426 100644
--- a/chrome/test/data/extensions/api_test/tabs/basics/move.js
+++ b/chrome/test/data/extensions/api_test/tabs/basics/move.js
@@ -7,13 +7,6 @@
 var moveTabIds = {};
 var kChromeUINewTabURL = "chrome://newtab/";
 
-var newTabUrls = [
-  kChromeUINewTabURL,
-  // The tab URL will be redirected to the Local New Tab Page if
-  // features::kUseGoogleLocalNtp is not enabled.
-  'chrome-search://local-ntp/local-ntp.html',
-];
-
 chrome.test.runTests([
   // Do a series of moves and removes so that we get the following
   //
@@ -39,8 +32,7 @@
       }));
       chrome.tabs.getAllInWindow(firstWindowId, pass(function(tabs) {
         assertEq(pages.length, tabs.length);
-        assertTrue(newTabUrls.includes(tabs[0].url));
-        for (var i = 1; i < tabs.length; i++) {
+        for (var i in tabs) {
           assertEq(pages[i], tabs[i].url);
         }
       }));
@@ -52,7 +44,7 @@
     function checkMoveResults() {
       chrome.tabs.getAllInWindow(firstWindowId, pass(function(tabs) {
         assertEq(4, tabs.length);
-        assertTrue(newTabUrls.includes(tabs[0].url));
+        assertEq(kChromeUINewTabURL, tabs[0].url);
         assertEq(pageUrl("a"), tabs[1].url);
         assertEq(pageUrl("e"), tabs[2].url);
         assertEq(pageUrl("c"), tabs[3].url);
@@ -60,7 +52,7 @@
         chrome.tabs.getAllInWindow(secondWindowId, pass(function(tabs) {
           assertEq(3, tabs.length);
           assertEq(pageUrl("b"), tabs[0].url);
-          assertTrue(newTabUrls.includes(tabs[1].url));
+          assertEq(kChromeUINewTabURL, tabs[1].url);
           assertEq(pageUrl("d"), tabs[2].url);
         }));
       }));
@@ -86,14 +78,14 @@
     function checkMoveResults() {
       chrome.tabs.getAllInWindow(firstWindowId, pass(function(tabs) {
         assertEq(3, tabs.length);
-        assertTrue(newTabUrls.includes(tabs[0].url));
+        assertEq(kChromeUINewTabURL, tabs[0].url);
         assertEq(pageUrl("a"), tabs[1].url);
         assertEq(pageUrl("c"), tabs[2].url);
 
         chrome.tabs.getAllInWindow(secondWindowId, pass(function(tabs) {
           assertEq(4, tabs.length);
           assertEq(pageUrl("b"), tabs[0].url);
-          assertTrue(newTabUrls.includes(tabs[1].url));
+          assertEq(kChromeUINewTabURL, tabs[1].url);
           assertEq(pageUrl("d"), tabs[2].url);
           assertEq(pageUrl("e"), tabs[3].url);
         }));
@@ -114,7 +106,7 @@
                                  pass(function(tabs) {
         assertEq(3, tabs.length);
         assertEq(pageUrl("b"), tabs[0].url);
-        assertTrue(newTabUrls.includes(tabs[1].url));
+        assertEq(kChromeUINewTabURL, tabs[1].url);
         assertEq(pageUrl("e"), tabs[2].url);
       }));
     }));
@@ -143,7 +135,7 @@
         assertEq(3, tabs.length);
         assertEq(pageUrl("b"), tabs[0].url);
         assertEq(pageUrl("a"), tabs[1].url);
-        assertTrue(newTabUrls.includes(tabs[2].url));
+        assertEq(kChromeUINewTabURL, tabs[2].url);
       }));
     }));
   },
diff --git a/chrome/test/data/extensions/api_test/tabs/on_updated/test.js b/chrome/test/data/extensions/api_test/tabs/on_updated/test.js
index 0399058..7fcd443f 100644
--- a/chrome/test/data/extensions/api_test/tabs/on_updated/test.js
+++ b/chrome/test/data/extensions/api_test/tabs/on_updated/test.js
@@ -47,15 +47,19 @@
     chrome.tabs.create({ url: getURL('browserThenRendererInitiated/a.html') });
   },
 
-  function chromeUrls() {
+  function newTab() {
     // Test for crbug.com/27208.
+    //
+    // Note the two title settings. That is expected and due to the unusual way
+    // the NTP code ensures a set title.
     expect([
-      { status: 'loading', url: 'chrome://chrome-urls/' },
-      { title : "Chrome URLs" },
+      { status: 'loading', url: 'chrome://newtab/' },
+      { title : "New Tab" },
+      { title : "New Tab" },
       { status: 'complete' }
     ]);
 
-    chrome.tabs.create({ url: 'chrome://chrome-urls/' });
+    chrome.tabs.create({ url: 'chrome://newtab/' });
   },
 
   /*
diff --git a/chrome/test/data/extensions/api_test/webrequest/test_types.js b/chrome/test/data/extensions/api_test/webrequest/test_types.js
index bbbf877..5790a27 100644
--- a/chrome/test/data/extensions/api_test/webrequest/test_types.js
+++ b/chrome/test/data/extensions/api_test/webrequest/test_types.js
@@ -152,6 +152,7 @@
           // tabId 0 = tab opened by test runner;
           // tabId 1 = this tab.
           tabId: 1,
+          initiator: "null",
         }
       },
       { label: 'onBeforeSendHeaders',
@@ -162,6 +163,7 @@
           frameId: 1,
           parentFrameId: 0,
           tabId: 1,
+          initiator: "null",
         },
       },
       { label: 'onSendHeaders',
@@ -172,6 +174,7 @@
           frameId: 1,
           parentFrameId: 0,
           tabId: 1,
+          initiator: "null",
         },
       },
       { label: 'onHeadersReceived',
@@ -184,6 +187,7 @@
           tabId: 1,
           statusLine: 'HTTP/1.1 200 OK',
           statusCode: 200,
+          initiator: "null",
         },
       },
       { label: 'onResponseStarted',
@@ -198,6 +202,7 @@
           fromCache: false,
           statusLine: 'HTTP/1.1 200 OK',
           statusCode: 200,
+          initiator: "null",
         },
       },
       { label: 'onCompleted',
@@ -212,6 +217,7 @@
           fromCache: false,
           statusLine: 'HTTP/1.1 200 OK',
           statusCode: 200,
+          initiator: "null",
         },
       }],
       [['onBeforeRequest', 'onBeforeSendHeaders', 'onSendHeaders',
diff --git a/chrome/utility/chrome_content_utility_client.cc b/chrome/utility/chrome_content_utility_client.cc
index ede5cac..51cc352 100644
--- a/chrome/utility/chrome_content_utility_client.cc
+++ b/chrome/utility/chrome_content_utility_client.cc
@@ -264,7 +264,7 @@
 #endif  // BUILDFLAG(ENABLE_EXTENSIONS)
 
 #if defined(OS_CHROMEOS)
-  // TODO(jamescook): Figure out why we have to do this when not using --mash.
+  // TODO(jamescook): Figure out why we have to do this when not using mash.
   mash_service_factory_->RegisterOutOfProcessServices(services);
 #endif
 }
diff --git a/chrome/utility/mash_service_factory.cc b/chrome/utility/mash_service_factory.cc
index ec52286..b011308f 100644
--- a/chrome/utility/mash_service_factory.cc
+++ b/chrome/utility/mash_service_factory.cc
@@ -34,7 +34,7 @@
 }
 
 // Runs on the UI service main thread.
-// NOTE: For --mus the UI service is created at the //chrome/browser layer,
+// NOTE: For mus the UI service is created at the //chrome/browser layer,
 // not in //content. See ServiceManagerContext.
 std::unique_ptr<service_manager::Service> CreateUiService(
     const scoped_refptr<base::SingleThreadTaskRunner>& resource_runner,
diff --git a/chrome/utility/mash_service_factory.h b/chrome/utility/mash_service_factory.h
index 6cb915c5..20dcc7d 100644
--- a/chrome/utility/mash_service_factory.h
+++ b/chrome/utility/mash_service_factory.h
@@ -19,7 +19,7 @@
   MashServiceFactory();
   ~MashServiceFactory();
 
-  // Registers out-of-process services for --mash.
+  // Registers out-of-process services for mash.
   void RegisterOutOfProcessServices(
       content::ContentUtilityClient::StaticServiceMap* services);
 
diff --git a/chromecast/browser/cast_display_configurator.cc b/chromecast/browser/cast_display_configurator.cc
index ced1009..632ba62 100644
--- a/chromecast/browser/cast_display_configurator.cc
+++ b/chromecast/browser/cast_display_configurator.cc
@@ -63,16 +63,31 @@
   gfx::Point origin;
   delegate_->Configure(
       *display, display->native_mode(), origin,
-      base::Bind(&CastDisplayConfigurator::OnDisplayConfigured,
-                 weak_factory_.GetWeakPtr(),
-                 gfx::Rect(origin, display->native_mode()->size())));
+      base::BindRepeating(&CastDisplayConfigurator::OnDisplayConfigured,
+                          weak_factory_.GetWeakPtr(), display,
+                          display->native_mode(), origin));
 }
 
-void CastDisplayConfigurator::OnDisplayConfigured(const gfx::Rect& bounds,
-                                                  bool success) {
+void CastDisplayConfigurator::OnDisplayConfigured(
+    display::DisplaySnapshot* display,
+    const display::DisplayMode* mode,
+    const gfx::Point& origin,
+    bool success) {
+  DCHECK(display);
+  DCHECK(mode);
+
+  const gfx::Rect bounds(origin, mode->size());
   VLOG(1) << __func__ << " success=" << success
           << " bounds=" << bounds.ToString();
-  cast_screen_->OnDisplayChanged(1.0f, bounds);
+  if (success) {
+    // Need to update the display state otherwise it becomes stale.
+    display->set_current_mode(mode);
+    display->set_origin(origin);
+
+    cast_screen_->OnDisplayChanged(1.0f, bounds);
+  } else {
+    LOG(FATAL) << "Failed to configure display";
+  }
 }
 
 }  // namespace shell
diff --git a/chromecast/browser/cast_display_configurator.h b/chromecast/browser/cast_display_configurator.h
index 2e4dc23..36b042f8 100644
--- a/chromecast/browser/cast_display_configurator.h
+++ b/chromecast/browser/cast_display_configurator.h
@@ -13,12 +13,13 @@
 #include "ui/display/types/native_display_observer.h"
 
 namespace display {
+class DisplayMode;
 class DisplaySnapshot;
 class NativeDisplayDelegate;
 }  // namespace display
 
 namespace gfx {
-class Rect;
+class Point;
 }  // namespace gfx
 
 namespace chromecast {
@@ -44,7 +45,10 @@
  private:
   void OnDisplaysAcquired(
       const std::vector<display::DisplaySnapshot*>& displays);
-  void OnDisplayConfigured(const gfx::Rect& bounds, bool success);
+  void OnDisplayConfigured(display::DisplaySnapshot* display,
+                           const display::DisplayMode* mode,
+                           const gfx::Point& origin,
+                           bool success);
 
   std::unique_ptr<display::NativeDisplayDelegate> delegate_;
   CastScreen* const cast_screen_;
diff --git a/chromeos/assistant/assistant.gni b/chromeos/assistant/assistant.gni
index 0b500ae3..eae72a7 100644
--- a/chromeos/assistant/assistant.gni
+++ b/chromeos/assistant/assistant.gni
@@ -1,7 +1,7 @@
 declare_args() {
   # Enable assistant features. Assistant related code is compiled only when
   # true. The default assistant implementation is a stub.
-  enable_cros_assistant = false
+  enable_cros_assistant = is_chromeos
 
   # Enable assistant implementation based on libassistant. This requires
   # enable_cros_assistant also enabled.
diff --git a/chromeos/components/tether/BUILD.gn b/chromeos/components/tether/BUILD.gn
index 7f92831..4d7a6ca6 100644
--- a/chromeos/components/tether/BUILD.gn
+++ b/chromeos/components/tether/BUILD.gn
@@ -75,6 +75,8 @@
     "host_scan_scheduler_impl.h",
     "host_scanner.cc",
     "host_scanner.h",
+    "host_scanner_impl.cc",
+    "host_scanner_impl.h",
     "host_scanner_operation.cc",
     "host_scanner_operation.h",
     "hotspot_usage_duration_tracker.cc",
@@ -187,6 +189,8 @@
     "fake_host_scan_cache.h",
     "fake_host_scan_scheduler.cc",
     "fake_host_scan_scheduler.h",
+    "fake_host_scanner.cc",
+    "fake_host_scanner.h",
     "fake_network_configuration_remover.cc",
     "fake_network_configuration_remover.h",
     "fake_notification_presenter.cc",
@@ -259,8 +263,8 @@
     "host_scan_cache_unittest.cc",
     "host_scan_device_prioritizer_impl_unittest.cc",
     "host_scan_scheduler_impl_unittest.cc",
+    "host_scanner_impl_unittest.cc",
     "host_scanner_operation_unittest.cc",
-    "host_scanner_unittest.cc",
     "hotspot_usage_duration_tracker_unittest.cc",
     "keep_alive_operation_unittest.cc",
     "keep_alive_scheduler_unittest.cc",
diff --git a/chromeos/components/tether/fake_host_scanner.cc b/chromeos/components/tether/fake_host_scanner.cc
new file mode 100644
index 0000000..8ce518c
--- /dev/null
+++ b/chromeos/components/tether/fake_host_scanner.cc
@@ -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.
+
+#include "chromeos/components/tether/fake_host_scanner.h"
+
+namespace chromeos {
+
+namespace tether {
+
+FakeHostScanner::FakeHostScanner() = default;
+
+FakeHostScanner::~FakeHostScanner() = default;
+
+void FakeHostScanner::StopScan() {
+  bool was_active = is_active_;
+  is_active_ = false;
+
+  if (was_active)
+    NotifyScanFinished();
+}
+
+void FakeHostScanner::NotifyScanFinished() {
+  HostScanner::NotifyScanFinished();
+}
+
+bool FakeHostScanner::IsScanActive() {
+  return is_active_;
+}
+
+void FakeHostScanner::StartScan() {
+  ++num_scans_started_;
+  is_active_ = true;
+}
+
+}  // namespace tether
+
+}  // namespace chromeos
diff --git a/chromeos/components/tether/fake_host_scanner.h b/chromeos/components/tether/fake_host_scanner.h
new file mode 100644
index 0000000..ff39f6ff
--- /dev/null
+++ b/chromeos/components/tether/fake_host_scanner.h
@@ -0,0 +1,40 @@
+// 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_COMPONENTS_TETHER_FAKE_HOST_SCANNER_H_
+#define CHROMEOS_COMPONENTS_TETHER_FAKE_HOST_SCANNER_H_
+
+#include "chromeos/components/tether/host_scanner.h"
+
+namespace chromeos {
+
+namespace tether {
+
+// Test double for HostScanner.
+class FakeHostScanner : public HostScanner {
+ public:
+  FakeHostScanner();
+  ~FakeHostScanner() override;
+
+  size_t num_scans_started() { return num_scans_started_; }
+
+  void StopScan();
+  void NotifyScanFinished();
+
+  // HostScanner:
+  bool IsScanActive() override;
+  void StartScan() override;
+
+ private:
+  size_t num_scans_started_ = 0u;
+  bool is_active_ = false;
+
+  DISALLOW_COPY_AND_ASSIGN(FakeHostScanner);
+};
+
+}  // namespace tether
+
+}  // namespace chromeos
+
+#endif  // CHROMEOS_COMPONENTS_TETHER_FAKE_HOST_SCANNER_H_
diff --git a/chromeos/components/tether/host_scan_scheduler_impl_unittest.cc b/chromeos/components/tether/host_scan_scheduler_impl_unittest.cc
index bb8d1e2b..a25bc516 100644
--- a/chromeos/components/tether/host_scan_scheduler_impl_unittest.cc
+++ b/chromeos/components/tether/host_scan_scheduler_impl_unittest.cc
@@ -6,13 +6,14 @@
 
 #include <memory>
 
+#include "base/memory/ptr_util.h"
 #include "base/run_loop.h"
 #include "base/test/histogram_tester.h"
 #include "base/test/scoped_task_environment.h"
 #include "base/test/simple_test_clock.h"
 #include "base/test/test_simple_task_runner.h"
 #include "base/timer/mock_timer.h"
-#include "chromeos/components/tether/host_scanner.h"
+#include "chromeos/components/tether/fake_host_scanner.h"
 #include "chromeos/dbus/dbus_thread_manager.h"
 #include "chromeos/dbus/power_manager_client.h"
 #include "chromeos/login/login_state.h"
@@ -31,41 +32,6 @@
 
 namespace {
 
-class FakeHostScanner : public HostScanner {
- public:
-  FakeHostScanner()
-      : HostScanner(nullptr,
-                    nullptr,
-                    nullptr,
-                    nullptr,
-                    nullptr,
-                    nullptr,
-                    nullptr,
-                    nullptr,
-                    nullptr,
-                    nullptr),
-        num_scans_started_(0) {}
-  ~FakeHostScanner() override = default;
-
-  void StartScan() override {
-    is_scan_active_ = true;
-    num_scans_started_++;
-  }
-
-  void StopScan() {
-    is_scan_active_ = false;
-    NotifyScanFinished();
-  }
-
-  bool IsScanActive() override { return is_scan_active_; }
-
-  int num_scans_started() { return num_scans_started_; }
-
- private:
-  bool is_scan_active_ = false;
-  int num_scans_started_ = 0;
-};
-
 const char kEthernetServiceGuid[] = "ethernetServiceGuid";
 const char kWifiServiceGuid[] = "wifiServiceGuid";
 const char kTetherGuid[] = "tetherGuid";
@@ -196,13 +162,13 @@
 
 TEST_F(HostScanSchedulerImplTest, ScheduleScan) {
   host_scan_scheduler_->ScheduleScan();
-  EXPECT_EQ(1, fake_host_scanner_->num_scans_started());
+  EXPECT_EQ(1u, fake_host_scanner_->num_scans_started());
   EXPECT_TRUE(
       network_state_handler()->GetScanningByType(NetworkTypePattern::Tether()));
 
   test_clock_->Advance(base::TimeDelta::FromSeconds(5));
   fake_host_scanner_->StopScan();
-  EXPECT_EQ(1, fake_host_scanner_->num_scans_started());
+  EXPECT_EQ(1u, fake_host_scanner_->num_scans_started());
   EXPECT_FALSE(
       network_state_handler()->GetScanningByType(NetworkTypePattern::Tether()));
 
@@ -214,19 +180,19 @@
 TEST_F(HostScanSchedulerImplTest, ScanRequested) {
   // Begin scanning.
   RequestScan();
-  EXPECT_EQ(1, fake_host_scanner_->num_scans_started());
+  EXPECT_EQ(1u, fake_host_scanner_->num_scans_started());
   EXPECT_TRUE(
       network_state_handler()->GetScanningByType(NetworkTypePattern::Tether()));
 
   // Should not begin a new scan while a scan is active.
   RequestScan();
-  EXPECT_EQ(1, fake_host_scanner_->num_scans_started());
+  EXPECT_EQ(1u, fake_host_scanner_->num_scans_started());
   EXPECT_TRUE(
       network_state_handler()->GetScanningByType(NetworkTypePattern::Tether()));
 
   test_clock_->Advance(base::TimeDelta::FromSeconds(5));
   fake_host_scanner_->StopScan();
-  EXPECT_EQ(1, fake_host_scanner_->num_scans_started());
+  EXPECT_EQ(1u, fake_host_scanner_->num_scans_started());
   EXPECT_FALSE(
       network_state_handler()->GetScanningByType(NetworkTypePattern::Tether()));
   mock_timer_->Fire();
@@ -234,7 +200,7 @@
 
   // A new scan should be allowed once a scan is not active.
   RequestScan();
-  EXPECT_EQ(2, fake_host_scanner_->num_scans_started());
+  EXPECT_EQ(2u, fake_host_scanner_->num_scans_started());
   EXPECT_TRUE(
       network_state_handler()->GetScanningByType(NetworkTypePattern::Tether()));
 }
@@ -313,17 +279,17 @@
   // When no Tether network is present, a scan should start when the default
   // network is disconnected.
   SetEthernetNetworkConnecting();
-  EXPECT_EQ(0, fake_host_scanner_->num_scans_started());
+  EXPECT_EQ(0u, fake_host_scanner_->num_scans_started());
   EXPECT_FALSE(
       network_state_handler()->GetScanningByType(NetworkTypePattern::Tether()));
 
   SetEthernetNetworkConnected();
-  EXPECT_EQ(0, fake_host_scanner_->num_scans_started());
+  EXPECT_EQ(0u, fake_host_scanner_->num_scans_started());
   EXPECT_FALSE(
       network_state_handler()->GetScanningByType(NetworkTypePattern::Tether()));
 
   SetEthernetNetworkDisconnected(std::string() /* default_service_path */);
-  EXPECT_EQ(1, fake_host_scanner_->num_scans_started());
+  EXPECT_EQ(1u, fake_host_scanner_->num_scans_started());
   EXPECT_TRUE(
       network_state_handler()->GetScanningByType(NetworkTypePattern::Tether()));
 
@@ -332,22 +298,22 @@
   // When Tether is present but disconnected, a scan should start when the
   // default network is disconnected.
   std::string tether_service_path = AddTetherNetworkState();
-  EXPECT_EQ(1, fake_host_scanner_->num_scans_started());
+  EXPECT_EQ(1u, fake_host_scanner_->num_scans_started());
   EXPECT_FALSE(
       network_state_handler()->GetScanningByType(NetworkTypePattern::Tether()));
 
   SetEthernetNetworkConnecting();
-  EXPECT_EQ(1, fake_host_scanner_->num_scans_started());
+  EXPECT_EQ(1u, fake_host_scanner_->num_scans_started());
   EXPECT_FALSE(
       network_state_handler()->GetScanningByType(NetworkTypePattern::Tether()));
 
   SetEthernetNetworkConnected();
-  EXPECT_EQ(1, fake_host_scanner_->num_scans_started());
+  EXPECT_EQ(1u, fake_host_scanner_->num_scans_started());
   EXPECT_FALSE(
       network_state_handler()->GetScanningByType(NetworkTypePattern::Tether()));
 
   SetEthernetNetworkDisconnected(std::string() /* default_service_path */);
-  EXPECT_EQ(2, fake_host_scanner_->num_scans_started());
+  EXPECT_EQ(2u, fake_host_scanner_->num_scans_started());
   EXPECT_TRUE(
       network_state_handler()->GetScanningByType(NetworkTypePattern::Tether()));
 
@@ -357,22 +323,22 @@
   // Ethernet network becomes the default network and then disconnects.
   network_state_handler()->SetTetherNetworkStateConnecting(kTetherGuid);
 
-  EXPECT_EQ(2, fake_host_scanner_->num_scans_started());
+  EXPECT_EQ(2u, fake_host_scanner_->num_scans_started());
   EXPECT_FALSE(
       network_state_handler()->GetScanningByType(NetworkTypePattern::Tether()));
 
   SetEthernetNetworkConnecting();
-  EXPECT_EQ(2, fake_host_scanner_->num_scans_started());
+  EXPECT_EQ(2u, fake_host_scanner_->num_scans_started());
   EXPECT_FALSE(
       network_state_handler()->GetScanningByType(NetworkTypePattern::Tether()));
 
   SetEthernetNetworkConnected();
-  EXPECT_EQ(2, fake_host_scanner_->num_scans_started());
+  EXPECT_EQ(2u, fake_host_scanner_->num_scans_started());
   EXPECT_FALSE(
       network_state_handler()->GetScanningByType(NetworkTypePattern::Tether()));
 
   SetEthernetNetworkDisconnected(tether_service_path);
-  EXPECT_EQ(2, fake_host_scanner_->num_scans_started());
+  EXPECT_EQ(2u, fake_host_scanner_->num_scans_started());
   EXPECT_FALSE(
       network_state_handler()->GetScanningByType(NetworkTypePattern::Tether()));
 
@@ -383,22 +349,22 @@
   base::RunLoop().RunUntilIdle();
   network_state_handler()->SetTetherNetworkStateConnected(kTetherGuid);
 
-  EXPECT_EQ(2, fake_host_scanner_->num_scans_started());
+  EXPECT_EQ(2u, fake_host_scanner_->num_scans_started());
   EXPECT_FALSE(
       network_state_handler()->GetScanningByType(NetworkTypePattern::Tether()));
 
   SetEthernetNetworkConnecting();
-  EXPECT_EQ(2, fake_host_scanner_->num_scans_started());
+  EXPECT_EQ(2u, fake_host_scanner_->num_scans_started());
   EXPECT_FALSE(
       network_state_handler()->GetScanningByType(NetworkTypePattern::Tether()));
 
   SetEthernetNetworkConnected();
-  EXPECT_EQ(2, fake_host_scanner_->num_scans_started());
+  EXPECT_EQ(2u, fake_host_scanner_->num_scans_started());
   EXPECT_FALSE(
       network_state_handler()->GetScanningByType(NetworkTypePattern::Tether()));
 
   SetEthernetNetworkDisconnected(tether_service_path);
-  EXPECT_EQ(2, fake_host_scanner_->num_scans_started());
+  EXPECT_EQ(2u, fake_host_scanner_->num_scans_started());
   EXPECT_FALSE(
       network_state_handler()->GetScanningByType(NetworkTypePattern::Tether()));
 }
diff --git a/chromeos/components/tether/host_scanner.cc b/chromeos/components/tether/host_scanner.cc
index 7c98b5f..9178fe1 100644
--- a/chromeos/components/tether/host_scanner.cc
+++ b/chromeos/components/tether/host_scanner.cc
@@ -1,138 +1,17 @@
-// Copyright 2014 The Chromium Authors. All rights reserved.
+// 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.
 
 #include "chromeos/components/tether/host_scanner.h"
 
-#include <algorithm>
-
-#include "base/bind.h"
-#include "base/metrics/histogram_macros.h"
-#include "chromeos/components/tether/device_id_tether_network_guid_map.h"
-#include "chromeos/components/tether/device_status_util.h"
-#include "chromeos/components/tether/gms_core_notifications_state_tracker_impl.h"
-#include "chromeos/components/tether/host_scan_cache.h"
-#include "chromeos/components/tether/master_host_scan_cache.h"
-#include "chromeos/components/tether/tether_host_fetcher.h"
-#include "chromeos/network/network_state.h"
-#include "components/cryptauth/remote_device_loader.h"
-#include "components/proximity_auth/logging/logging.h"
-
 namespace chromeos {
 
 namespace tether {
 
-HostScanner::HostScanner(
-    NetworkStateHandler* network_state_handler,
-    TetherHostFetcher* tether_host_fetcher,
-    BleConnectionManager* connection_manager,
-    HostScanDevicePrioritizer* host_scan_device_prioritizer,
-    TetherHostResponseRecorder* tether_host_response_recorder,
-    GmsCoreNotificationsStateTrackerImpl* gms_core_notifications_state_tracker,
-    NotificationPresenter* notification_presenter,
-    DeviceIdTetherNetworkGuidMap* device_id_tether_network_guid_map,
-    HostScanCache* host_scan_cache,
-    base::Clock* clock)
-    : network_state_handler_(network_state_handler),
-      tether_host_fetcher_(tether_host_fetcher),
-      connection_manager_(connection_manager),
-      host_scan_device_prioritizer_(host_scan_device_prioritizer),
-      tether_host_response_recorder_(tether_host_response_recorder),
-      gms_core_notifications_state_tracker_(
-          gms_core_notifications_state_tracker),
-      notification_presenter_(notification_presenter),
-      device_id_tether_network_guid_map_(device_id_tether_network_guid_map),
-      host_scan_cache_(host_scan_cache),
-      clock_(clock),
-      weak_ptr_factory_(this) {}
+HostScanner::HostScanner() = default;
 
 HostScanner::~HostScanner() = default;
 
-bool HostScanner::IsScanActive() {
-  return is_fetching_hosts_ || host_scanner_operation_;
-}
-
-void HostScanner::StartScan() {
-  if (IsScanActive())
-    return;
-
-  is_fetching_hosts_ = true;
-  tether_host_fetcher_->FetchAllTetherHosts(base::Bind(
-      &HostScanner::OnTetherHostsFetched, weak_ptr_factory_.GetWeakPtr()));
-}
-
-void HostScanner::OnTetherHostsFetched(
-    const cryptauth::RemoteDeviceList& tether_hosts) {
-  is_fetching_hosts_ = false;
-
-  if (tether_hosts.empty()) {
-    PA_LOG(WARNING) << "Could not start host scan. No tether hosts available.";
-    return;
-  }
-
-  PA_LOG(INFO) << "Starting Tether host scan. " << tether_hosts.size() << " "
-               << "potential hosts included in the search.";
-
-  tether_guids_in_cache_before_scan_ =
-      host_scan_cache_->GetTetherGuidsInCache();
-
-  host_scanner_operation_ = HostScannerOperation::Factory::NewInstance(
-      tether_hosts, connection_manager_, host_scan_device_prioritizer_,
-      tether_host_response_recorder_);
-  // Add |gms_core_notifications_state_tracker_| as the first observer. When the
-  // final change event is emitted, this class will destroy
-  // |host_scanner_operation_|, so |gms_core_notifications_state_tracker_| must
-  // be notified of the final change event before that occurs.
-  host_scanner_operation_->AddObserver(gms_core_notifications_state_tracker_);
-  host_scanner_operation_->AddObserver(this);
-  host_scanner_operation_->Initialize();
-}
-
-void HostScanner::OnTetherAvailabilityResponse(
-    const std::vector<HostScannerOperation::ScannedDeviceInfo>&
-        scanned_device_list_so_far,
-    const std::vector<cryptauth::RemoteDevice>&
-        gms_core_notifications_disabled_devices,
-    bool is_final_scan_result) {
-  if (scanned_device_list_so_far.empty() && !is_final_scan_result) {
-    was_notification_showing_when_current_scan_started_ =
-        IsPotentialHotspotNotificationShowing();
-  }
-
-  // Ensure all results received so far are in the cache (setting entries which
-  // already exist is a no-op).
-  for (const auto& scanned_device_info : scanned_device_list_so_far)
-    SetCacheEntry(scanned_device_info);
-
-  if (CanAvailableHostNotificationBeShown() &&
-      !scanned_device_list_so_far.empty()) {
-    if (scanned_device_list_so_far.size() == 1u &&
-        (notification_presenter_->GetPotentialHotspotNotificationState() !=
-             NotificationPresenter::PotentialHotspotNotificationState::
-                 MULTIPLE_HOTSPOTS_NEARBY_SHOWN ||
-         is_final_scan_result)) {
-      const cryptauth::RemoteDevice& remote_device =
-          scanned_device_list_so_far.at(0).remote_device;
-      int32_t signal_strength;
-      NormalizeDeviceStatus(scanned_device_list_so_far.at(0).device_status,
-                            nullptr /* carrier */,
-                            nullptr /* battery_percentage */, &signal_strength);
-      notification_presenter_->NotifyPotentialHotspotNearby(remote_device,
-                                                            signal_strength);
-    } else {
-      // Note: If a single-device notification was previously displayed, calling
-      // NotifyMultiplePotentialHotspotsNearby() will reuse the existing
-      // notification.
-      notification_presenter_->NotifyMultiplePotentialHotspotsNearby();
-    }
-
-    was_notification_shown_in_current_scan_ = true;
-  }
-
-  if (is_final_scan_result)
-    OnFinalScanResultReceived(scanned_device_list_so_far);
-}
-
 void HostScanner::AddObserver(Observer* observer) {
   observer_list_.AddObserver(observer);
 }
@@ -146,136 +25,6 @@
     observer.ScanFinished();
 }
 
-void HostScanner::SetCacheEntry(
-    const HostScannerOperation::ScannedDeviceInfo& scanned_device_info) {
-  const DeviceStatus& status = scanned_device_info.device_status;
-  const cryptauth::RemoteDevice& remote_device =
-      scanned_device_info.remote_device;
-
-  std::string carrier;
-  int32_t battery_percentage;
-  int32_t signal_strength;
-  NormalizeDeviceStatus(status, &carrier, &battery_percentage,
-                        &signal_strength);
-
-  host_scan_cache_->SetHostScanResult(
-      *HostScanCacheEntry::Builder()
-           .SetTetherNetworkGuid(device_id_tether_network_guid_map_
-                                     ->GetTetherNetworkGuidForDeviceId(
-                                         remote_device.GetDeviceId()))
-           .SetDeviceName(remote_device.name)
-           .SetCarrier(carrier)
-           .SetBatteryPercentage(battery_percentage)
-           .SetSignalStrength(signal_strength)
-           .SetSetupRequired(scanned_device_info.setup_required)
-           .Build());
-}
-
-void HostScanner::OnFinalScanResultReceived(
-    const std::vector<HostScannerOperation::ScannedDeviceInfo>&
-        final_scan_results) {
-  // Search through all GUIDs that were in the cache before the scan began. If
-  // any of those GUIDs are not present in the final scan results, remove them
-  // from the cache.
-  for (const auto& tether_guid_in_cache : tether_guids_in_cache_before_scan_) {
-    bool is_guid_in_final_scan_results = false;
-
-    for (const auto& scan_result : final_scan_results) {
-      if (device_id_tether_network_guid_map_->GetTetherNetworkGuidForDeviceId(
-              scan_result.remote_device.GetDeviceId()) ==
-          tether_guid_in_cache) {
-        is_guid_in_final_scan_results = true;
-        break;
-      }
-    }
-
-    if (!is_guid_in_final_scan_results)
-      host_scan_cache_->RemoveHostScanResult(tether_guid_in_cache);
-  }
-
-  if (final_scan_results.empty()) {
-    RecordHostScanResult(HostScanResultEventType::NO_HOSTS_FOUND);
-  } else if (!was_notification_shown_in_current_scan_) {
-    RecordHostScanResult(
-        HostScanResultEventType::HOSTS_FOUND_BUT_NO_NOTIFICATION_SHOWN);
-  } else if (final_scan_results.size() == 1u) {
-    RecordHostScanResult(
-        HostScanResultEventType::NOTIFICATION_SHOWN_SINGLE_HOST);
-  } else {
-    RecordHostScanResult(
-        HostScanResultEventType::NOTIFICATION_SHOWN_MULTIPLE_HOSTS);
-  }
-  has_notification_been_shown_in_previous_scan_ |=
-      was_notification_shown_in_current_scan_;
-  was_notification_shown_in_current_scan_ = false;
-  was_notification_showing_when_current_scan_started_ = false;
-
-  PA_LOG(INFO) << "Finished Tether host scan. " << final_scan_results.size()
-               << " result(s) were found.";
-
-  // If the final scan result has been received, the operation is finished.
-  // Delete it.
-  host_scanner_operation_->RemoveObserver(
-      gms_core_notifications_state_tracker_);
-  host_scanner_operation_->RemoveObserver(this);
-  host_scanner_operation_.reset();
-
-  NotifyScanFinished();
-}
-
-void HostScanner::RecordHostScanResult(HostScanResultEventType event_type) {
-  DCHECK(event_type != HostScanResultEventType::HOST_SCAN_RESULT_MAX);
-  UMA_HISTOGRAM_ENUMERATION("InstantTethering.HostScanResult", event_type,
-                            HostScanResultEventType::HOST_SCAN_RESULT_MAX);
-}
-
-bool HostScanner::IsPotentialHotspotNotificationShowing() {
-  return notification_presenter_->GetPotentialHotspotNotificationState() !=
-         NotificationPresenter::PotentialHotspotNotificationState::
-             NO_HOTSPOT_NOTIFICATION_SHOWN;
-}
-
-bool HostScanner::CanAvailableHostNotificationBeShown() {
-  // Note: If a network is active (i.e., connecting or connected), it will be
-  // returned at the front of the list, so using FirstNetworkByType() guarantees
-  // that we will find an active network if there is one.
-  const chromeos::NetworkState* first_network =
-      network_state_handler_->FirstNetworkByType(
-          chromeos::NetworkTypePattern::Default());
-  if (first_network && first_network->IsConnectingOrConnected()) {
-    // If a network is connecting or connected, the notification should not be
-    // shown.
-    return false;
-  }
-
-  if (!IsPotentialHotspotNotificationShowing() &&
-      was_notification_shown_in_current_scan_) {
-    // If a notification was shown in the current scan but it is no longer
-    // showing, it has been removed, either due to NotificationRemover or due to
-    // the user closing it. Since a scan only lasts on the order of seconds to
-    // tens of seconds, we know that the notification was very recently closed,
-    // so we should not re-show it.
-    return false;
-  }
-
-  if (!IsPotentialHotspotNotificationShowing() &&
-      was_notification_showing_when_current_scan_started_) {
-    // If a notification was showing when the scan started but is no longer
-    // showing, it has been removed and should not be re-shown.
-    return false;
-  }
-
-  if (has_notification_been_shown_in_previous_scan_ &&
-      !was_notification_showing_when_current_scan_started_) {
-    // If a notification was shown in a previous scan but was not visible when
-    // the current scan started, it should not be shown because this could be
-    // considered spammy; see crbug.com/759078.
-    return false;
-  }
-
-  return true;
-}
-
 }  // namespace tether
 
 }  // namespace chromeos
diff --git a/chromeos/components/tether/host_scanner.h b/chromeos/components/tether/host_scanner.h
index 5d172add..b2d188d 100644
--- a/chromeos/components/tether/host_scanner.h
+++ b/chromeos/components/tether/host_scanner.h
@@ -5,120 +5,41 @@
 #ifndef CHROMEOS_COMPONENTS_TETHER_HOST_SCANNER_H_
 #define CHROMEOS_COMPONENTS_TETHER_HOST_SCANNER_H_
 
-#include <string>
-#include <unordered_set>
-#include <vector>
-
-#include "base/gtest_prod_util.h"
-#include "base/memory/weak_ptr.h"
 #include "base/observer_list.h"
-#include "base/time/clock.h"
-#include "base/time/time.h"
-#include "chromeos/components/tether/host_scanner_operation.h"
-#include "chromeos/components/tether/notification_presenter.h"
-#include "chromeos/network/network_state_handler.h"
-#include "components/cryptauth/remote_device.h"
 
 namespace chromeos {
 
 namespace tether {
 
-class BleConnectionManager;
-class DeviceIdTetherNetworkGuidMap;
-class GmsCoreNotificationsStateTrackerImpl;
-class HostScanCache;
-class HostScanDevicePrioritizer;
-class TetherHostFetcher;
-class TetherHostResponseRecorder;
-
-// Scans for nearby tether hosts. When StartScan() is called, this class creates
-// a new HostScannerOperation and uses it to contact nearby devices to query
-// whether they can provide tether capabilities. Once the scan results are
-// received, they are stored in the HostScanCache passed to the constructor,
-// and observers are notified via HostScanner::Observer::ScanFinished().
-class HostScanner : public HostScannerOperation::Observer {
+// Scans for nearby tether hosts.
+class HostScanner {
  public:
   class Observer {
    public:
-    void virtual ScanFinished() = 0;
+    virtual void ScanFinished() = 0;
+
+   protected:
+    virtual ~Observer() = default;
   };
 
-  HostScanner(NetworkStateHandler* network_state_handler,
-              TetherHostFetcher* tether_host_fetcher,
-              BleConnectionManager* connection_manager,
-              HostScanDevicePrioritizer* host_scan_device_prioritizer,
-              TetherHostResponseRecorder* tether_host_response_recorder,
-              GmsCoreNotificationsStateTrackerImpl*
-                  gms_core_notifications_state_tracker,
-              NotificationPresenter* notification_presenter,
-              DeviceIdTetherNetworkGuidMap* device_id_tether_network_guid_map,
-              HostScanCache* host_scan_cache,
-              base::Clock* clock);
+  HostScanner();
   virtual ~HostScanner();
 
   // Returns true if a scan is currently in progress.
-  virtual bool IsScanActive();
+  virtual bool IsScanActive() = 0;
 
   // Starts a host scan if there is no current scan. If a scan is active, this
   // function is a no-op.
-  virtual void StartScan();
-
-  void NotifyScanFinished();
-
-  // HostScannerOperation::Observer:
-  void OnTetherAvailabilityResponse(
-      const std::vector<HostScannerOperation::ScannedDeviceInfo>&
-          scanned_device_list_so_far,
-      const std::vector<cryptauth::RemoteDevice>&
-          gms_core_notifications_disabled_devices,
-      bool is_final_scan_result) override;
+  virtual void StartScan() = 0;
 
   void AddObserver(Observer* observer);
   void RemoveObserver(Observer* observer);
 
+ protected:
+  void NotifyScanFinished();
+
  private:
-  friend class HostScannerTest;
-  friend class HostScanSchedulerTest;
-  FRIEND_TEST_ALL_PREFIXES(HostScannerTest, TestScan_ResultsFromNoDevices);
-
-  enum HostScanResultEventType {
-    NO_HOSTS_FOUND = 0,
-    NOTIFICATION_SHOWN_SINGLE_HOST = 1,
-    NOTIFICATION_SHOWN_MULTIPLE_HOSTS = 2,
-    HOSTS_FOUND_BUT_NO_NOTIFICATION_SHOWN = 3,
-    HOST_SCAN_RESULT_MAX
-  };
-
-  void OnTetherHostsFetched(const cryptauth::RemoteDeviceList& tether_hosts);
-  void SetCacheEntry(
-      const HostScannerOperation::ScannedDeviceInfo& scanned_device_info);
-  void OnFinalScanResultReceived(
-      const std::vector<HostScannerOperation::ScannedDeviceInfo>&
-          final_scan_results);
-  void RecordHostScanResult(HostScanResultEventType event_type);
-  bool IsPotentialHotspotNotificationShowing();
-  bool CanAvailableHostNotificationBeShown();
-
-  NetworkStateHandler* network_state_handler_;
-  TetherHostFetcher* tether_host_fetcher_;
-  BleConnectionManager* connection_manager_;
-  HostScanDevicePrioritizer* host_scan_device_prioritizer_;
-  TetherHostResponseRecorder* tether_host_response_recorder_;
-  GmsCoreNotificationsStateTrackerImpl* gms_core_notifications_state_tracker_;
-  NotificationPresenter* notification_presenter_;
-  DeviceIdTetherNetworkGuidMap* device_id_tether_network_guid_map_;
-  HostScanCache* host_scan_cache_;
-  base::Clock* clock_;
-
-  bool is_fetching_hosts_ = false;
-  bool was_notification_showing_when_current_scan_started_ = false;
-  bool was_notification_shown_in_current_scan_ = false;
-  bool has_notification_been_shown_in_previous_scan_ = false;
-  std::unique_ptr<HostScannerOperation> host_scanner_operation_;
-  std::unordered_set<std::string> tether_guids_in_cache_before_scan_;
-
   base::ObserverList<Observer> observer_list_;
-  base::WeakPtrFactory<HostScanner> weak_ptr_factory_;
 
   DISALLOW_COPY_AND_ASSIGN(HostScanner);
 };
diff --git a/chromeos/components/tether/host_scanner_impl.cc b/chromeos/components/tether/host_scanner_impl.cc
new file mode 100644
index 0000000..6e4b827
--- /dev/null
+++ b/chromeos/components/tether/host_scanner_impl.cc
@@ -0,0 +1,268 @@
+// 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/components/tether/host_scanner_impl.h"
+
+#include <algorithm>
+
+#include "base/bind.h"
+#include "base/metrics/histogram_macros.h"
+#include "chromeos/components/tether/device_id_tether_network_guid_map.h"
+#include "chromeos/components/tether/device_status_util.h"
+#include "chromeos/components/tether/gms_core_notifications_state_tracker_impl.h"
+#include "chromeos/components/tether/host_scan_cache.h"
+#include "chromeos/components/tether/master_host_scan_cache.h"
+#include "chromeos/components/tether/tether_host_fetcher.h"
+#include "chromeos/network/network_state.h"
+#include "components/cryptauth/remote_device_loader.h"
+#include "components/proximity_auth/logging/logging.h"
+
+namespace chromeos {
+
+namespace tether {
+
+HostScannerImpl::HostScannerImpl(
+    NetworkStateHandler* network_state_handler,
+    TetherHostFetcher* tether_host_fetcher,
+    BleConnectionManager* connection_manager,
+    HostScanDevicePrioritizer* host_scan_device_prioritizer,
+    TetherHostResponseRecorder* tether_host_response_recorder,
+    GmsCoreNotificationsStateTrackerImpl* gms_core_notifications_state_tracker,
+    NotificationPresenter* notification_presenter,
+    DeviceIdTetherNetworkGuidMap* device_id_tether_network_guid_map,
+    HostScanCache* host_scan_cache,
+    base::Clock* clock)
+    : network_state_handler_(network_state_handler),
+      tether_host_fetcher_(tether_host_fetcher),
+      connection_manager_(connection_manager),
+      host_scan_device_prioritizer_(host_scan_device_prioritizer),
+      tether_host_response_recorder_(tether_host_response_recorder),
+      gms_core_notifications_state_tracker_(
+          gms_core_notifications_state_tracker),
+      notification_presenter_(notification_presenter),
+      device_id_tether_network_guid_map_(device_id_tether_network_guid_map),
+      host_scan_cache_(host_scan_cache),
+      clock_(clock),
+      weak_ptr_factory_(this) {}
+
+HostScannerImpl::~HostScannerImpl() = default;
+
+bool HostScannerImpl::IsScanActive() {
+  return is_fetching_hosts_ || host_scanner_operation_;
+}
+
+void HostScannerImpl::StartScan() {
+  if (IsScanActive())
+    return;
+
+  is_fetching_hosts_ = true;
+  tether_host_fetcher_->FetchAllTetherHosts(base::Bind(
+      &HostScannerImpl::OnTetherHostsFetched, weak_ptr_factory_.GetWeakPtr()));
+}
+
+void HostScannerImpl::OnTetherHostsFetched(
+    const cryptauth::RemoteDeviceList& tether_hosts) {
+  is_fetching_hosts_ = false;
+
+  if (tether_hosts.empty()) {
+    PA_LOG(WARNING) << "Could not start host scan. No tether hosts available.";
+    return;
+  }
+
+  PA_LOG(INFO) << "Starting Tether host scan. " << tether_hosts.size() << " "
+               << "potential hosts included in the search.";
+
+  tether_guids_in_cache_before_scan_ =
+      host_scan_cache_->GetTetherGuidsInCache();
+
+  host_scanner_operation_ = HostScannerOperation::Factory::NewInstance(
+      tether_hosts, connection_manager_, host_scan_device_prioritizer_,
+      tether_host_response_recorder_);
+  // Add |gms_core_notifications_state_tracker_| as the first observer. When the
+  // final change event is emitted, this class will destroy
+  // |host_scanner_operation_|, so |gms_core_notifications_state_tracker_| must
+  // be notified of the final change event before that occurs.
+  host_scanner_operation_->AddObserver(gms_core_notifications_state_tracker_);
+  host_scanner_operation_->AddObserver(this);
+  host_scanner_operation_->Initialize();
+}
+
+void HostScannerImpl::OnTetherAvailabilityResponse(
+    const std::vector<HostScannerOperation::ScannedDeviceInfo>&
+        scanned_device_list_so_far,
+    const std::vector<cryptauth::RemoteDevice>&
+        gms_core_notifications_disabled_devices,
+    bool is_final_scan_result) {
+  if (scanned_device_list_so_far.empty() && !is_final_scan_result) {
+    was_notification_showing_when_current_scan_started_ =
+        IsPotentialHotspotNotificationShowing();
+  }
+
+  // Ensure all results received so far are in the cache (setting entries which
+  // already exist is a no-op).
+  for (const auto& scanned_device_info : scanned_device_list_so_far)
+    SetCacheEntry(scanned_device_info);
+
+  if (CanAvailableHostNotificationBeShown() &&
+      !scanned_device_list_so_far.empty()) {
+    if (scanned_device_list_so_far.size() == 1u &&
+        (notification_presenter_->GetPotentialHotspotNotificationState() !=
+             NotificationPresenter::PotentialHotspotNotificationState::
+                 MULTIPLE_HOTSPOTS_NEARBY_SHOWN ||
+         is_final_scan_result)) {
+      const cryptauth::RemoteDevice& remote_device =
+          scanned_device_list_so_far.at(0).remote_device;
+      int32_t signal_strength;
+      NormalizeDeviceStatus(scanned_device_list_so_far.at(0).device_status,
+                            nullptr /* carrier */,
+                            nullptr /* battery_percentage */, &signal_strength);
+      notification_presenter_->NotifyPotentialHotspotNearby(remote_device,
+                                                            signal_strength);
+    } else {
+      // Note: If a single-device notification was previously displayed, calling
+      // NotifyMultiplePotentialHotspotsNearby() will reuse the existing
+      // notification.
+      notification_presenter_->NotifyMultiplePotentialHotspotsNearby();
+    }
+
+    was_notification_shown_in_current_scan_ = true;
+  }
+
+  if (is_final_scan_result)
+    OnFinalScanResultReceived(scanned_device_list_so_far);
+}
+
+void HostScannerImpl::SetCacheEntry(
+    const HostScannerOperation::ScannedDeviceInfo& scanned_device_info) {
+  const DeviceStatus& status = scanned_device_info.device_status;
+  const cryptauth::RemoteDevice& remote_device =
+      scanned_device_info.remote_device;
+
+  std::string carrier;
+  int32_t battery_percentage;
+  int32_t signal_strength;
+  NormalizeDeviceStatus(status, &carrier, &battery_percentage,
+                        &signal_strength);
+
+  host_scan_cache_->SetHostScanResult(
+      *HostScanCacheEntry::Builder()
+           .SetTetherNetworkGuid(device_id_tether_network_guid_map_
+                                     ->GetTetherNetworkGuidForDeviceId(
+                                         remote_device.GetDeviceId()))
+           .SetDeviceName(remote_device.name)
+           .SetCarrier(carrier)
+           .SetBatteryPercentage(battery_percentage)
+           .SetSignalStrength(signal_strength)
+           .SetSetupRequired(scanned_device_info.setup_required)
+           .Build());
+}
+
+void HostScannerImpl::OnFinalScanResultReceived(
+    const std::vector<HostScannerOperation::ScannedDeviceInfo>&
+        final_scan_results) {
+  // Search through all GUIDs that were in the cache before the scan began. If
+  // any of those GUIDs are not present in the final scan results, remove them
+  // from the cache.
+  for (const auto& tether_guid_in_cache : tether_guids_in_cache_before_scan_) {
+    bool is_guid_in_final_scan_results = false;
+
+    for (const auto& scan_result : final_scan_results) {
+      if (device_id_tether_network_guid_map_->GetTetherNetworkGuidForDeviceId(
+              scan_result.remote_device.GetDeviceId()) ==
+          tether_guid_in_cache) {
+        is_guid_in_final_scan_results = true;
+        break;
+      }
+    }
+
+    if (!is_guid_in_final_scan_results)
+      host_scan_cache_->RemoveHostScanResult(tether_guid_in_cache);
+  }
+
+  if (final_scan_results.empty()) {
+    RecordHostScanResult(HostScanResultEventType::NO_HOSTS_FOUND);
+  } else if (!was_notification_shown_in_current_scan_) {
+    RecordHostScanResult(
+        HostScanResultEventType::HOSTS_FOUND_BUT_NO_NOTIFICATION_SHOWN);
+  } else if (final_scan_results.size() == 1u) {
+    RecordHostScanResult(
+        HostScanResultEventType::NOTIFICATION_SHOWN_SINGLE_HOST);
+  } else {
+    RecordHostScanResult(
+        HostScanResultEventType::NOTIFICATION_SHOWN_MULTIPLE_HOSTS);
+  }
+  has_notification_been_shown_in_previous_scan_ |=
+      was_notification_shown_in_current_scan_;
+  was_notification_shown_in_current_scan_ = false;
+  was_notification_showing_when_current_scan_started_ = false;
+
+  PA_LOG(INFO) << "Finished Tether host scan. " << final_scan_results.size()
+               << " result(s) were found.";
+
+  // If the final scan result has been received, the operation is finished.
+  // Delete it.
+  host_scanner_operation_->RemoveObserver(
+      gms_core_notifications_state_tracker_);
+  host_scanner_operation_->RemoveObserver(this);
+  host_scanner_operation_.reset();
+
+  NotifyScanFinished();
+}
+
+void HostScannerImpl::RecordHostScanResult(HostScanResultEventType event_type) {
+  DCHECK(event_type != HostScanResultEventType::HOST_SCAN_RESULT_MAX);
+  UMA_HISTOGRAM_ENUMERATION("InstantTethering.HostScanResult", event_type,
+                            HostScanResultEventType::HOST_SCAN_RESULT_MAX);
+}
+
+bool HostScannerImpl::IsPotentialHotspotNotificationShowing() {
+  return notification_presenter_->GetPotentialHotspotNotificationState() !=
+         NotificationPresenter::PotentialHotspotNotificationState::
+             NO_HOTSPOT_NOTIFICATION_SHOWN;
+}
+
+bool HostScannerImpl::CanAvailableHostNotificationBeShown() {
+  // Note: If a network is active (i.e., connecting or connected), it will be
+  // returned at the front of the list, so using FirstNetworkByType() guarantees
+  // that we will find an active network if there is one.
+  const chromeos::NetworkState* first_network =
+      network_state_handler_->FirstNetworkByType(
+          chromeos::NetworkTypePattern::Default());
+  if (first_network && first_network->IsConnectingOrConnected()) {
+    // If a network is connecting or connected, the notification should not be
+    // shown.
+    return false;
+  }
+
+  if (!IsPotentialHotspotNotificationShowing() &&
+      was_notification_shown_in_current_scan_) {
+    // If a notification was shown in the current scan but it is no longer
+    // showing, it has been removed, either due to NotificationRemover or due to
+    // the user closing it. Since a scan only lasts on the order of seconds to
+    // tens of seconds, we know that the notification was very recently closed,
+    // so we should not re-show it.
+    return false;
+  }
+
+  if (!IsPotentialHotspotNotificationShowing() &&
+      was_notification_showing_when_current_scan_started_) {
+    // If a notification was showing when the scan started but is no longer
+    // showing, it has been removed and should not be re-shown.
+    return false;
+  }
+
+  if (has_notification_been_shown_in_previous_scan_ &&
+      !was_notification_showing_when_current_scan_started_) {
+    // If a notification was shown in a previous scan but was not visible when
+    // the current scan started, it should not be shown because this could be
+    // considered spammy; see crbug.com/759078.
+    return false;
+  }
+
+  return true;
+}
+
+}  // namespace tether
+
+}  // namespace chromeos
diff --git a/chromeos/components/tether/host_scanner_impl.h b/chromeos/components/tether/host_scanner_impl.h
new file mode 100644
index 0000000..d99e9d52
--- /dev/null
+++ b/chromeos/components/tether/host_scanner_impl.h
@@ -0,0 +1,125 @@
+// 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_COMPONENTS_TETHER_HOST_SCANNER_IMPL_H_
+#define CHROMEOS_COMPONENTS_TETHER_HOST_SCANNER_IMPL_H_
+
+#include <string>
+#include <unordered_set>
+#include <vector>
+
+#include "base/gtest_prod_util.h"
+#include "base/memory/weak_ptr.h"
+#include "base/observer_list.h"
+#include "base/time/clock.h"
+#include "base/time/time.h"
+#include "chromeos/components/tether/host_scanner.h"
+#include "chromeos/components/tether/host_scanner_operation.h"
+#include "chromeos/components/tether/notification_presenter.h"
+#include "chromeos/network/network_state_handler.h"
+#include "components/cryptauth/remote_device.h"
+
+namespace chromeos {
+
+namespace tether {
+
+class BleConnectionManager;
+class DeviceIdTetherNetworkGuidMap;
+class GmsCoreNotificationsStateTrackerImpl;
+class HostScanCache;
+class HostScanDevicePrioritizer;
+class TetherHostFetcher;
+class TetherHostResponseRecorder;
+
+// Scans for nearby tether hosts. When StartScan() is called, this class creates
+// a new HostScannerOperation and uses it to contact nearby devices to query
+// whether they can provide tether capabilities. Once the scan results are
+// received, they are stored in the HostScanCache passed to the constructor,
+// and observers are notified via HostScanner::Observer::ScanFinished().
+class HostScannerImpl : public HostScanner,
+                        public HostScannerOperation::Observer {
+ public:
+  class Observer {
+   public:
+    void virtual ScanFinished() = 0;
+  };
+
+  HostScannerImpl(
+      NetworkStateHandler* network_state_handler,
+      TetherHostFetcher* tether_host_fetcher,
+      BleConnectionManager* connection_manager,
+      HostScanDevicePrioritizer* host_scan_device_prioritizer,
+      TetherHostResponseRecorder* tether_host_response_recorder,
+      GmsCoreNotificationsStateTrackerImpl*
+          gms_core_notifications_state_tracker,
+      NotificationPresenter* notification_presenter,
+      DeviceIdTetherNetworkGuidMap* device_id_tether_network_guid_map,
+      HostScanCache* host_scan_cache,
+      base::Clock* clock);
+  ~HostScannerImpl() override;
+
+  // HostScanner:
+  bool IsScanActive() override;
+  void StartScan() override;
+
+ protected:
+  // HostScannerOperation::Observer:
+  void OnTetherAvailabilityResponse(
+      const std::vector<HostScannerOperation::ScannedDeviceInfo>&
+          scanned_device_list_so_far,
+      const std::vector<cryptauth::RemoteDevice>&
+          gms_core_notifications_disabled_devices,
+      bool is_final_scan_result) override;
+
+ private:
+  friend class HostScannerImplTest;
+  FRIEND_TEST_ALL_PREFIXES(HostScannerImplTest, TestScan_ResultsFromNoDevices);
+
+  enum HostScanResultEventType {
+    NO_HOSTS_FOUND = 0,
+    NOTIFICATION_SHOWN_SINGLE_HOST = 1,
+    NOTIFICATION_SHOWN_MULTIPLE_HOSTS = 2,
+    HOSTS_FOUND_BUT_NO_NOTIFICATION_SHOWN = 3,
+    HOST_SCAN_RESULT_MAX
+  };
+
+  void OnTetherHostsFetched(const cryptauth::RemoteDeviceList& tether_hosts);
+  void SetCacheEntry(
+      const HostScannerOperation::ScannedDeviceInfo& scanned_device_info);
+  void OnFinalScanResultReceived(
+      const std::vector<HostScannerOperation::ScannedDeviceInfo>&
+          final_scan_results);
+  void RecordHostScanResult(HostScanResultEventType event_type);
+  bool IsPotentialHotspotNotificationShowing();
+  bool CanAvailableHostNotificationBeShown();
+
+  NetworkStateHandler* network_state_handler_;
+  TetherHostFetcher* tether_host_fetcher_;
+  BleConnectionManager* connection_manager_;
+  HostScanDevicePrioritizer* host_scan_device_prioritizer_;
+  TetherHostResponseRecorder* tether_host_response_recorder_;
+  GmsCoreNotificationsStateTrackerImpl* gms_core_notifications_state_tracker_;
+  NotificationPresenter* notification_presenter_;
+  DeviceIdTetherNetworkGuidMap* device_id_tether_network_guid_map_;
+  HostScanCache* host_scan_cache_;
+  base::Clock* clock_;
+
+  bool is_fetching_hosts_ = false;
+  bool was_notification_showing_when_current_scan_started_ = false;
+  bool was_notification_shown_in_current_scan_ = false;
+  bool has_notification_been_shown_in_previous_scan_ = false;
+  std::unique_ptr<HostScannerOperation> host_scanner_operation_;
+  std::unordered_set<std::string> tether_guids_in_cache_before_scan_;
+
+  base::ObserverList<Observer> observer_list_;
+  base::WeakPtrFactory<HostScannerImpl> weak_ptr_factory_;
+
+  DISALLOW_COPY_AND_ASSIGN(HostScannerImpl);
+};
+
+}  // namespace tether
+
+}  // namespace chromeos
+
+#endif  // CHROMEOS_COMPONENTS_TETHER_HOST_SCANNER_IMPL_H_
diff --git a/chromeos/components/tether/host_scanner_unittest.cc b/chromeos/components/tether/host_scanner_impl_unittest.cc
similarity index 96%
rename from chromeos/components/tether/host_scanner_unittest.cc
rename to chromeos/components/tether/host_scanner_impl_unittest.cc
index 878fec6..db1d98a 100644
--- a/chromeos/components/tether/host_scanner_unittest.cc
+++ b/chromeos/components/tether/host_scanner_impl_unittest.cc
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "chromeos/components/tether/host_scanner.h"
+#include "chromeos/components/tether/host_scanner_impl.h"
 
 #include <algorithm>
 #include <memory>
@@ -181,9 +181,9 @@
 
 }  // namespace
 
-class HostScannerTest : public NetworkStateTest {
+class HostScannerImplTest : public NetworkStateTest {
  protected:
-  HostScannerTest()
+  HostScannerImplTest()
       : test_devices_(cryptauth::GenerateTestRemoteDevices(4)),
         test_scanned_device_infos(CreateFakeScannedDeviceInfos(test_devices_)) {
   }
@@ -217,7 +217,7 @@
 
     test_clock_ = std::make_unique<base::SimpleTestClock>();
 
-    host_scanner_ = base::WrapUnique(new HostScanner(
+    host_scanner_ = base::WrapUnique(new HostScannerImpl(
         network_state_handler(), fake_tether_host_fetcher_.get(),
         fake_ble_connection_manager_.get(),
         fake_host_scan_device_prioritizer_.get(),
@@ -275,19 +275,19 @@
         fake_notification_presenter_->GetPotentialHotspotNotificationState());
 
     if (is_final_scan_result) {
-      HostScanner::HostScanResultEventType expected_event_type =
-          HostScanner::HostScanResultEventType::NO_HOSTS_FOUND;
+      HostScannerImpl::HostScanResultEventType expected_event_type =
+          HostScannerImpl::HostScanResultEventType::NO_HOSTS_FOUND;
       if (!scanned_device_infos_from_current_scan_.empty() &&
           expected_notification_state ==
               NotificationPresenter::PotentialHotspotNotificationState::
                   NO_HOTSPOT_NOTIFICATION_SHOWN) {
-        expected_event_type = HostScanner::HostScanResultEventType::
+        expected_event_type = HostScannerImpl::HostScanResultEventType::
             HOSTS_FOUND_BUT_NO_NOTIFICATION_SHOWN;
       } else if (scanned_device_infos_from_current_scan_.size() == 1) {
-        expected_event_type = HostScanner::HostScanResultEventType::
+        expected_event_type = HostScannerImpl::HostScanResultEventType::
             NOTIFICATION_SHOWN_SINGLE_HOST;
       } else if (scanned_device_infos_from_current_scan_.size() > 1) {
-        expected_event_type = HostScanner::HostScanResultEventType::
+        expected_event_type = HostScannerImpl::HostScanResultEventType::
             NOTIFICATION_SHOWN_MULTIPLE_HOSTS;
       }
       histogram_tester_.ExpectUniqueSample("InstantTethering.HostScanResult",
@@ -407,10 +407,10 @@
   base::HistogramTester histogram_tester_;
 
  private:
-  DISALLOW_COPY_AND_ASSIGN(HostScannerTest);
+  DISALLOW_COPY_AND_ASSIGN(HostScannerImplTest);
 };
 
-TEST_F(HostScannerTest, TestScan_ConnectingToExistingNetwork) {
+TEST_F(HostScannerImplTest, TestScan_ConnectingToExistingNetwork) {
   StartConnectingToWifiNetwork();
   EXPECT_TRUE(network_state_handler()->DefaultNetwork());
 
@@ -447,7 +447,7 @@
   EXPECT_FALSE(host_scanner_->IsScanActive());
 }
 
-TEST_F(HostScannerTest, TestNotificationNotDisplayedMultipleTimes) {
+TEST_F(HostScannerImplTest, TestNotificationNotDisplayedMultipleTimes) {
   StartConnectingToWifiNetwork();
   EXPECT_TRUE(network_state_handler()->DefaultNetwork());
 
@@ -484,7 +484,7 @@
   EXPECT_FALSE(host_scanner_->IsScanActive());
 }
 
-TEST_F(HostScannerTest, TestScan_ResultsFromAllDevices) {
+TEST_F(HostScannerImplTest, TestScan_ResultsFromAllDevices) {
   EXPECT_FALSE(host_scanner_->IsScanActive());
   host_scanner_->StartScan();
   EXPECT_TRUE(host_scanner_->IsScanActive());
@@ -518,7 +518,7 @@
   EXPECT_FALSE(host_scanner_->IsScanActive());
 }
 
-TEST_F(HostScannerTest, TestScan_ResultsFromNoDevices) {
+TEST_F(HostScannerImplTest, TestScan_ResultsFromNoDevices) {
   EXPECT_FALSE(host_scanner_->IsScanActive());
   host_scanner_->StartScan();
   EXPECT_TRUE(host_scanner_->IsScanActive());
@@ -535,10 +535,10 @@
 
   histogram_tester_.ExpectUniqueSample(
       "InstantTethering.HostScanResult",
-      HostScanner::HostScanResultEventType::NO_HOSTS_FOUND, 1);
+      HostScannerImpl::HostScanResultEventType::NO_HOSTS_FOUND, 1);
 }
 
-TEST_F(HostScannerTest, TestScan_ResultsFromSomeDevices) {
+TEST_F(HostScannerImplTest, TestScan_ResultsFromSomeDevices) {
   EXPECT_FALSE(host_scanner_->IsScanActive());
   host_scanner_->StartScan();
   EXPECT_TRUE(host_scanner_->IsScanActive());
@@ -568,7 +568,7 @@
   EXPECT_FALSE(host_scanner_->IsScanActive());
 }
 
-TEST_F(HostScannerTest, TestScan_MultipleScanCallsDuringOperation) {
+TEST_F(HostScannerImplTest, TestScan_MultipleScanCallsDuringOperation) {
   EXPECT_FALSE(host_scanner_->IsScanActive());
   host_scanner_->StartScan();
   EXPECT_TRUE(host_scanner_->IsScanActive());
@@ -611,7 +611,7 @@
   EXPECT_FALSE(host_scanner_->IsScanActive());
 }
 
-TEST_F(HostScannerTest, TestScan_MultipleCompleteScanSessions) {
+TEST_F(HostScannerImplTest, TestScan_MultipleCompleteScanSessions) {
   // Start the first scan session.
   EXPECT_FALSE(host_scanner_->IsScanActive());
   host_scanner_->StartScan();
diff --git a/chromeos/components/tether/synchronous_shutdown_object_container_impl.cc b/chromeos/components/tether/synchronous_shutdown_object_container_impl.cc
index b50eb6a..1197260 100644
--- a/chromeos/components/tether/synchronous_shutdown_object_container_impl.cc
+++ b/chromeos/components/tether/synchronous_shutdown_object_container_impl.cc
@@ -14,7 +14,7 @@
 #include "chromeos/components/tether/host_connection_metrics_logger.h"
 #include "chromeos/components/tether/host_scan_device_prioritizer_impl.h"
 #include "chromeos/components/tether/host_scan_scheduler_impl.h"
-#include "chromeos/components/tether/host_scanner.h"
+#include "chromeos/components/tether/host_scanner_impl.h"
 #include "chromeos/components/tether/hotspot_usage_duration_tracker.h"
 #include "chromeos/components/tether/keep_alive_scheduler.h"
 #include "chromeos/components/tether/master_host_scan_cache.h"
@@ -136,7 +136,7 @@
       hotspot_usage_duration_tracker_(
           std::make_unique<HotspotUsageDurationTracker>(active_host_.get(),
                                                         clock_.get())),
-      host_scanner_(std::make_unique<HostScanner>(
+      host_scanner_(std::make_unique<HostScannerImpl>(
           network_state_handler_,
           asychronous_container->tether_host_fetcher(),
           asychronous_container->ble_connection_manager(),
diff --git a/chromeos/dbus/fake_smb_provider_client.cc b/chromeos/dbus/fake_smb_provider_client.cc
index e1e442a8..a1657ab 100644
--- a/chromeos/dbus/fake_smb_provider_client.cc
+++ b/chromeos/dbus/fake_smb_provider_client.cc
@@ -123,4 +123,12 @@
       FROM_HERE, base::BindOnce(std::move(callback), smbprovider::ERROR_OK));
 }
 
+void FakeSmbProviderClient::CopyEntry(int32_t mount_id,
+                                      const base::FilePath& source_path,
+                                      const base::FilePath& target_path,
+                                      StatusCallback callback) {
+  base::ThreadTaskRunnerHandle::Get()->PostTask(
+      FROM_HERE, base::BindOnce(std::move(callback), smbprovider::ERROR_OK));
+}
+
 }  // namespace chromeos
diff --git a/chromeos/dbus/fake_smb_provider_client.h b/chromeos/dbus/fake_smb_provider_client.h
index 894861d..6692b36 100644
--- a/chromeos/dbus/fake_smb_provider_client.h
+++ b/chromeos/dbus/fake_smb_provider_client.h
@@ -71,6 +71,11 @@
                  const base::FilePath& target_path,
                  StatusCallback callback) override;
 
+  void CopyEntry(int32_t mount_id,
+                 const base::FilePath& source_path,
+                 const base::FilePath& target_path,
+                 StatusCallback callback) override;
+
  private:
   DISALLOW_COPY_AND_ASSIGN(FakeSmbProviderClient);
 };
diff --git a/chromeos/dbus/smb_provider_client.cc b/chromeos/dbus/smb_provider_client.cc
index 0a7580b..c53f945 100644
--- a/chromeos/dbus/smb_provider_client.cc
+++ b/chromeos/dbus/smb_provider_client.cc
@@ -197,6 +197,17 @@
     CallDefaultMethod(smbprovider::kMoveEntryMethod, options, &callback);
   }
 
+  void CopyEntry(int32_t mount_id,
+                 const base::FilePath& source_path,
+                 const base::FilePath& target_path,
+                 StatusCallback callback) override {
+    smbprovider::CopyEntryOptionsProto options;
+    options.set_mount_id(mount_id);
+    options.set_source_path(source_path.value());
+    options.set_target_path(target_path.value());
+    CallDefaultMethod(smbprovider::kCopyEntryMethod, options, &callback);
+  }
+
  protected:
   // DBusClient override.
   void Init(dbus::Bus* bus) override {
diff --git a/chromeos/dbus/smb_provider_client.h b/chromeos/dbus/smb_provider_client.h
index cfb30007..a67d564 100644
--- a/chromeos/dbus/smb_provider_client.h
+++ b/chromeos/dbus/smb_provider_client.h
@@ -136,6 +136,14 @@
                          const base::FilePath& target_path,
                          StatusCallback callback) = 0;
 
+  // Calls CopyEntry. Using the corresponding |mount_id|, this copies the entry
+  // at |source_path| to |target_path|. This operation will fail if the
+  // target already exists.
+  virtual void CopyEntry(int32_t mount_id,
+                         const base::FilePath& source_path,
+                         const base::FilePath& target_path,
+                         StatusCallback callback) = 0;
+
  protected:
   // Create() should be used instead.
   SmbProviderClient();
diff --git a/chromeos/services/BUILD.gn b/chromeos/services/BUILD.gn
new file mode 100644
index 0000000..aeec53b4
--- /dev/null
+++ b/chromeos/services/BUILD.gn
@@ -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.
+
+import("//build/config/ui.gni")
+import("//chromeos/assistant/assistant.gni")
+import("//services/catalog/public/tools/catalog.gni")
+import("//services/service_manager/public/tools/test/service_test.gni")
+import("//testing/test.gni")
+
+# This is modeled after //services:services_unittests
+# One Big Target for services to register their unit test sources. This exists
+# to avoid having to maintain a separate test binary for every service.
+#
+# To add tests for a new service, please define a "tests" source_set in the
+# service subdirectory and add it as a dependency here. If your unit tests
+# use the ServiceTest framework, you must also include corresponding catalog
+# entries in the "chromeos_services_unittests_catalog" target below.
+service_test("chromeos_services_unittests") {
+  deps = []
+
+  if (enable_cros_assistant) {
+    deps += [ "//chromeos/services/assistant:tests" ]
+  }
+
+  data_deps = [
+    "//testing/buildbot/filters:services_unittests_filters",
+  ]
+
+  catalog = ":chromeos_services_unittests_catalog"
+}
+
+catalog("chromeos_services_unittests_catalog") {
+  testonly = true
+
+  catalog_deps = []
+
+  if (enable_cros_assistant) {
+    catalog_deps += [ "//chromeos/services/assistant:tests_catalog" ]
+  }
+}
diff --git a/chromeos/services/assistant/BUILD.gn b/chromeos/services/assistant/BUILD.gn
index 7a1914bb..e342448 100644
--- a/chromeos/services/assistant/BUILD.gn
+++ b/chromeos/services/assistant/BUILD.gn
@@ -3,6 +3,7 @@
 # found in the LICENSE file.
 
 import("//chromeos/assistant/assistant.gni")
+import("//services/catalog/public/tools/catalog.gni")
 import("//services/service_manager/public/cpp/service.gni")
 import("//services/service_manager/public/service_manifest.gni")
 
@@ -16,15 +17,23 @@
     "service.h",
   ]
 
+  deps = [
+    "//chromeos",
+    "//mojo/public/cpp/bindings:bindings",
+    "//services/service_manager/public/cpp:cpp",
+  ]
+
   if (enable_cros_libassistant) {
     sources += [
       "assistant_manager_service_impl.cc",
       "assistant_manager_service_impl.h",
+      "platform/system_provider_impl.cc",
+      "platform/system_provider_impl.h",
       "platform_api_impl.cc",
       "platform_api_impl.h",
     ]
 
-    deps = [
+    deps += [
       "//chromeos/assistant/internal",
       "//libassistant/contrib/core",
       "//libassistant/contrib/platform/audio",
@@ -67,3 +76,33 @@
     "//services/service_manager/public/cpp",
   ]
 }
+
+source_set("tests") {
+  testonly = true
+  deps = [
+    ":lib",
+    "//base",
+    "//base/test:test_support",
+    "//mojo/public/cpp/bindings:bindings",
+    "//services/service_manager/public/cpp",
+    "//services/service_manager/public/cpp:service_test_support",
+    "//testing/gmock",
+    "//testing/gtest",
+  ]
+  sources = []
+
+  if (enable_cros_libassistant) {
+    sources += [ "platform/system_provider_impl_unittest.cc" ]
+  }
+}
+
+service_manifest("unittest_manifest") {
+  name = "assistant_unittests"
+  source = "unittest_manifest.json"
+  packaged_services = [ ":manifest" ]
+}
+
+catalog("tests_catalog") {
+  testonly = true
+  embedded_services = [ ":unittest_manifest" ]
+}
diff --git a/chromeos/services/assistant/OWNERS b/chromeos/services/assistant/OWNERS
index a5e67145..1f4b0556 100644
--- a/chromeos/services/assistant/OWNERS
+++ b/chromeos/services/assistant/OWNERS
@@ -2,3 +2,6 @@
 
 per-file manifest.json=set noparent
 per-file manifest.json=file://ipc/SECURITY_OWNERS
+
+per-file unittest_manifest.json=set noparent
+per-file unittest_manifest.json=file://ipc/SECURITY_OWNERS
diff --git a/chromeos/services/assistant/platform/system_provider_impl.cc b/chromeos/services/assistant/platform/system_provider_impl.cc
new file mode 100644
index 0000000..6b10051
--- /dev/null
+++ b/chromeos/services/assistant/platform/system_provider_impl.cc
@@ -0,0 +1,63 @@
+// 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/assistant/platform/system_provider_impl.h"
+
+#include "base/logging.h"
+#include "base/strings/string_util.h"
+#include "base/strings/stringprintf.h"
+#include "base/sys_info.h"
+#include "chromeos/system/version_loader.h"
+
+namespace chromeos {
+namespace assistant {
+
+SystemProviderImpl::SystemProviderImpl()
+    : board_name_(base::SysInfo::GetLsbReleaseBoard()),
+      device_model_id_(base::StringPrintf("cros-%s", board_name_.c_str())) {
+  embedder_build_info_ = chromeos::version_loader::GetVersion(
+      chromeos::version_loader::VERSION_FULL);
+}
+
+SystemProviderImpl::~SystemProviderImpl() = default;
+
+std::string SystemProviderImpl::GetDeviceModelId() {
+  return device_model_id_;
+}
+
+int SystemProviderImpl::GetDeviceModelRevision() {
+  return 0;
+}
+
+std::string SystemProviderImpl::GetEmbedderBuildInfo() {
+  return embedder_build_info_;
+}
+
+std::string SystemProviderImpl::GetBoardName() {
+  return board_name_;
+}
+
+std::string SystemProviderImpl::GetBoardRevision() {
+  return "0";
+}
+
+int SystemProviderImpl::GetDebugServerPort() {
+#if DCHECK_IS_ON()
+  return 8007;
+#else   // DCHECK_IS_ON()
+  // -1 disables debug server.
+  return -1;
+#endif  // DCHECK_IS_ON()
+}
+
+std::string SystemProviderImpl::GetOemDeviceId() {
+  return board_name_;
+}
+
+std::string SystemProviderImpl::GetDisplayName() {
+  return board_name_;
+}
+
+}  // namespace assistant
+}  // namespace chromeos
diff --git a/chromeos/services/assistant/platform/system_provider_impl.h b/chromeos/services/assistant/platform/system_provider_impl.h
new file mode 100644
index 0000000..256d01c6
--- /dev/null
+++ b/chromeos/services/assistant/platform/system_provider_impl.h
@@ -0,0 +1,42 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROMEOS_SERVICES_ASSISTANT_PLATFORM_SYSTEM_PROVIDER_IMPL_H_
+#define CHROMEOS_SERVICES_ASSISTANT_PLATFORM_SYSTEM_PROVIDER_IMPL_H_
+
+#include <string>
+
+#include "base/macros.h"
+#include "libassistant/shared/public/platform_system.h"
+
+namespace chromeos {
+namespace assistant {
+
+class SystemProviderImpl : public assistant_client::SystemProvider {
+ public:
+  SystemProviderImpl();
+  ~SystemProviderImpl() override;
+
+  // assistant_client::SystemProvider implementation:
+  std::string GetDeviceModelId() override;
+  int GetDeviceModelRevision() override;
+  std::string GetEmbedderBuildInfo() override;
+  std::string GetBoardName() override;
+  std::string GetBoardRevision() override;
+  std::string GetOemDeviceId() override;
+  std::string GetDisplayName() override;
+  int GetDebugServerPort() override;
+
+ private:
+  std::string board_name_;
+  std::string device_model_id_;
+  std::string embedder_build_info_;
+
+  DISALLOW_COPY_AND_ASSIGN(SystemProviderImpl);
+};
+
+}  // namespace assistant
+}  // namespace chromeos
+
+#endif  // CHROMEOS_SERVICES_ASSISTANT_PLATFORM_SYSTEM_PROVIDER_IMPL_H_
diff --git a/chromeos/services/assistant/platform/system_provider_impl_unittest.cc b/chromeos/services/assistant/platform/system_provider_impl_unittest.cc
new file mode 100644
index 0000000..7bd7fc6c
--- /dev/null
+++ b/chromeos/services/assistant/platform/system_provider_impl_unittest.cc
@@ -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.
+
+#include "chromeos/services/assistant/platform/system_provider_impl.h"
+
+#include "base/logging.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace chromeos {
+namespace assistant {
+
+class SystemProviderImplTest : public testing::Test {
+ public:
+  SystemProviderImplTest() {}
+
+  SystemProviderImpl* system_provider_() { return &system_provider_impl_; }
+
+ private:
+  SystemProviderImpl system_provider_impl_;
+
+  DISALLOW_COPY_AND_ASSIGN(SystemProviderImplTest);
+};
+
+TEST_F(SystemProviderImplTest, DebugServerOnForDebugBuild) {
+#if DCHECK_IS_ON()
+  ASSERT_GT(system_provider_()->GetDebugServerPort(), 0);
+#endif  // DCHECK_IS_ON()
+}
+
+TEST_F(SystemProviderImplTest, DebugServerOffForReleaseBuild) {
+#if !DCHECK_IS_ON()
+  ASSERT_LT(system_provider_()->GetDebugServerPort(), 0);
+#endif  // !DCHECK_IS_ON()
+}
+
+}  // namespace assistant
+}  // namespace chromeos
diff --git a/chromeos/services/assistant/platform_api_impl.cc b/chromeos/services/assistant/platform_api_impl.cc
index d48619a..57ed4c9 100644
--- a/chromeos/services/assistant/platform_api_impl.cc
+++ b/chromeos/services/assistant/platform_api_impl.cc
@@ -79,7 +79,7 @@
       file_provider_(config),
       network_provider_(config),
       resource_provider_(config),
-      system_provider_(config) {}
+      system_provider_() {}
 
 PlatformApiImpl::~PlatformApiImpl() = default;
 
diff --git a/chromeos/services/assistant/platform_api_impl.h b/chromeos/services/assistant/platform_api_impl.h
index ecacace..918742b 100644
--- a/chromeos/services/assistant/platform_api_impl.h
+++ b/chromeos/services/assistant/platform_api_impl.h
@@ -10,6 +10,7 @@
 #include <utility>
 #include <vector>
 
+#include "chromeos/services/assistant/platform/system_provider_impl.h"
 // TODO(xiaohuic): replace with "base/macros.h" once we remove
 // libassistant/contrib dependency.
 #include "libassistant/contrib/core/macros.h"
@@ -19,7 +20,6 @@
 #include "libassistant/contrib/platform/file/file_provider_impl.h"
 #include "libassistant/contrib/platform/net/network_provider_impl.h"
 #include "libassistant/contrib/platform/resources/resource_provider.h"
-#include "libassistant/contrib/platform/system/system_provider.h"
 #include "libassistant/shared/public/platform_api.h"
 
 namespace chromeos {
@@ -79,7 +79,7 @@
   assistant_contrib::FileProviderImpl file_provider_;
   assistant_contrib::NetworkProviderImpl network_provider_;
   assistant_contrib::ResourceProviderImpl resource_provider_;
-  assistant_contrib::SystemProviderImpl system_provider_;
+  SystemProviderImpl system_provider_;
 
   DISALLOW_COPY_AND_ASSIGN(PlatformApiImpl);
 };
diff --git a/chromeos/services/assistant/unittest_manifest.json b/chromeos/services/assistant/unittest_manifest.json
new file mode 100644
index 0000000..dadd11f
--- /dev/null
+++ b/chromeos/services/assistant/unittest_manifest.json
@@ -0,0 +1,16 @@
+{
+  "name": "assistant_unittests",
+  "display_name": "Assistant Unittests",
+  "interface_provider_specs": {
+    "service_manager:connector": {
+      "provides": {
+        "service_manager:service_factory": [
+          "service_manager::mojom::ServiceFactory"
+        ]
+      },
+      "requires": {
+        "assistant": []
+      }
+    }
+  }
+}
diff --git a/components/app_modal/javascript_dialog_manager.cc b/components/app_modal/javascript_dialog_manager.cc
index bfe42fe..6e7dc9f 100644
--- a/components/app_modal/javascript_dialog_manager.cc
+++ b/components/app_modal/javascript_dialog_manager.cc
@@ -162,7 +162,7 @@
 
 void JavaScriptDialogManager::RunJavaScriptDialog(
     content::WebContents* web_contents,
-    const GURL& alerting_frame_url,
+    content::RenderFrameHost* render_frame_host,
     content::JavaScriptDialogType dialog_type,
     const base::string16& message_text,
     const base::string16& default_prompt_text,
@@ -212,7 +212,8 @@
     last_close_time_ = base::TimeTicks();
   }
 
-  base::string16 dialog_title = GetTitle(web_contents, alerting_frame_url);
+  base::string16 dialog_title =
+      GetTitle(web_contents, render_frame_host->GetLastCommittedURL());
 
   extensions_client_->OnDialogOpened(web_contents);
 
diff --git a/components/app_modal/javascript_dialog_manager.h b/components/app_modal/javascript_dialog_manager.h
index e7975b2..57c1cac 100644
--- a/components/app_modal/javascript_dialog_manager.h
+++ b/components/app_modal/javascript_dialog_manager.h
@@ -44,7 +44,7 @@
 
   // JavaScriptDialogManager:
   void RunJavaScriptDialog(content::WebContents* web_contents,
-                           const GURL& alerting_frame_url,
+                           content::RenderFrameHost* render_frame_host,
                            content::JavaScriptDialogType dialog_type,
                            const base::string16& message_text,
                            const base::string16& default_prompt_text,
diff --git a/components/autofill/content/browser/content_autofill_driver.cc b/components/autofill/content/browser/content_autofill_driver.cc
index 74fd3edc..42209e8 100644
--- a/components/autofill/content/browser/content_autofill_driver.cc
+++ b/components/autofill/content/browser/content_autofill_driver.cc
@@ -42,11 +42,7 @@
   // AutofillManager isn't used if provider is valid, Autofill provider is
   // currently used by Android WebView only.
   if (provider) {
-    autofill_handler_ = std::make_unique<AutofillHandlerProxy>(this, provider);
-    GetAutofillAgent()->SetUserGestureRequired(false);
-    GetAutofillAgent()->SetSecureContextRequired(true);
-    GetAutofillAgent()->SetFocusRequiresScroll(false);
-    GetAutofillAgent()->SetQueryPasswordSuggestion(true);
+    SetAutofillProvider(provider);
   } else {
     autofill_handler_ = std::make_unique<AutofillManager>(
         this, client, app_locale, enable_download_manager);
@@ -304,4 +300,17 @@
   view->GetRenderWidgetHost()->RemoveKeyPressEventCallback(handler);
 }
 
+void ContentAutofillDriver::SetAutofillProvider(AutofillProvider* provider) {
+  autofill_handler_ = std::make_unique<AutofillHandlerProxy>(this, provider);
+  GetAutofillAgent()->SetUserGestureRequired(false);
+  GetAutofillAgent()->SetSecureContextRequired(true);
+  GetAutofillAgent()->SetFocusRequiresScroll(false);
+  GetAutofillAgent()->SetQueryPasswordSuggestion(true);
+}
+
+void ContentAutofillDriver::SetAutofillProviderForTesting(
+    AutofillProvider* provider) {
+  SetAutofillProvider(provider);
+}
+
 }  // namespace autofill
diff --git a/components/autofill/content/browser/content_autofill_driver.h b/components/autofill/content/browser/content_autofill_driver.h
index 17758e4..285f2f9 100644
--- a/components/autofill/content/browser/content_autofill_driver.h
+++ b/components/autofill/content/browser/content_autofill_driver.h
@@ -119,6 +119,8 @@
       const content::RenderWidgetHost::KeyPressEventCallback& handler);
   void RemoveKeyPressHandler();
 
+  void SetAutofillProviderForTesting(AutofillProvider* provider);
+
  protected:
   // Sets the manager to |manager| and sets |manager|'s external delegate
   // to |autofill_external_delegate_|. Takes ownership of |manager|.
@@ -131,6 +133,8 @@
   void RemoveHandler(
       const content::RenderWidgetHost::KeyPressEventCallback& handler) override;
 
+  void SetAutofillProvider(AutofillProvider* provider);
+
   // Weak ref to the RenderFrameHost the driver is associated with. Should
   // always be non-NULL and valid for lifetime of |this|.
   content::RenderFrameHost* const render_frame_host_;
diff --git a/components/autofill/content/common/autofill_types.mojom b/components/autofill/content/common/autofill_types.mojom
index 2720015..e46e37a 100644
--- a/components/autofill/content/common/autofill_types.mojom
+++ b/components/autofill/content/common/autofill_types.mojom
@@ -85,6 +85,20 @@
   FORM_SUBMISSION,
 };
 
+// autofill::FormFieldData::LabelSource
+enum LabelSource {
+  UNKNOWN,
+  LABEL_TAG,
+  P_TAG,
+  DIV_TABLE,
+  TD_TAG,
+  DD_TAG,
+  LI_TAG,
+  PLACE_HOLDER,
+  COMBINED,
+  VALUE,
+};
+
 // autofill::FormFieldData
 struct FormFieldData {
   string label;
@@ -107,6 +121,8 @@
 
   array<string> option_values;
   array<string> option_contents;
+
+  LabelSource label_source;
 };
 
 // autofill::FormData
diff --git a/components/autofill/content/common/autofill_types_struct_traits.cc b/components/autofill/content/common/autofill_types_struct_traits.cc
index ab0e334..3fa60aae 100644
--- a/components/autofill/content/common/autofill_types_struct_traits.cc
+++ b/components/autofill/content/common/autofill_types_struct_traits.cc
@@ -467,6 +467,79 @@
 }
 
 // static
+autofill::mojom::LabelSource
+EnumTraits<autofill::mojom::LabelSource, autofill::FormFieldData::LabelSource>::
+    ToMojom(autofill::FormFieldData::LabelSource input) {
+  switch (input) {
+    case autofill::FormFieldData::LabelSource::UNKNOWN:
+      return autofill::mojom::LabelSource::UNKNOWN;
+    case autofill::FormFieldData::LabelSource::LABEL_TAG:
+      return autofill::mojom::LabelSource::LABEL_TAG;
+    case autofill::FormFieldData::LabelSource::P_TAG:
+      return autofill::mojom::LabelSource::P_TAG;
+    case autofill::FormFieldData::LabelSource::DIV_TABLE:
+      return autofill::mojom::LabelSource::DIV_TABLE;
+    case autofill::FormFieldData::LabelSource::TD_TAG:
+      return autofill::mojom::LabelSource::TD_TAG;
+    case autofill::FormFieldData::LabelSource::DD_TAG:
+      return autofill::mojom::LabelSource::DD_TAG;
+    case autofill::FormFieldData::LabelSource::LI_TAG:
+      return autofill::mojom::LabelSource::LI_TAG;
+    case autofill::FormFieldData::LabelSource::PLACE_HOLDER:
+      return autofill::mojom::LabelSource::PLACE_HOLDER;
+    case autofill::FormFieldData::LabelSource::COMBINED:
+      return autofill::mojom::LabelSource::COMBINED;
+    case autofill::FormFieldData::LabelSource::VALUE:
+      return autofill::mojom::LabelSource::VALUE;
+  }
+
+  NOTREACHED();
+  return autofill::mojom::LabelSource::UNKNOWN;
+}
+
+// static
+bool EnumTraits<autofill::mojom::LabelSource,
+                autofill::FormFieldData::LabelSource>::
+    FromMojom(autofill::mojom::LabelSource input,
+              autofill::FormFieldData::LabelSource* output) {
+  switch (input) {
+    case autofill::mojom::LabelSource::UNKNOWN:
+      *output = autofill::FormFieldData::LabelSource::UNKNOWN;
+      return true;
+    case autofill::mojom::LabelSource::LABEL_TAG:
+      *output = autofill::FormFieldData::LabelSource::LABEL_TAG;
+      return true;
+    case autofill::mojom::LabelSource::P_TAG:
+      *output = autofill::FormFieldData::LabelSource::P_TAG;
+      return true;
+    case autofill::mojom::LabelSource::DIV_TABLE:
+      *output = autofill::FormFieldData::LabelSource::DIV_TABLE;
+      return true;
+    case autofill::mojom::LabelSource::TD_TAG:
+      *output = autofill::FormFieldData::LabelSource::TD_TAG;
+      return true;
+    case autofill::mojom::LabelSource::DD_TAG:
+      *output = autofill::FormFieldData::LabelSource::DD_TAG;
+      return true;
+    case autofill::mojom::LabelSource::LI_TAG:
+      *output = autofill::FormFieldData::LabelSource::LI_TAG;
+      return true;
+    case autofill::mojom::LabelSource::PLACE_HOLDER:
+      *output = autofill::FormFieldData::LabelSource::PLACE_HOLDER;
+      return true;
+    case autofill::mojom::LabelSource::COMBINED:
+      *output = autofill::FormFieldData::LabelSource::COMBINED;
+      return true;
+    case autofill::mojom::LabelSource::VALUE:
+      *output = autofill::FormFieldData::LabelSource::VALUE;
+      return true;
+  }
+
+  NOTREACHED();
+  return false;
+}
+
+// static
 bool StructTraits<
     autofill::mojom::FormFieldDataDataView,
     autofill::FormFieldData>::Read(autofill::mojom::FormFieldDataDataView data,
@@ -513,6 +586,9 @@
   if (!data.ReadOptionContents(&out->option_contents))
     return false;
 
+  if (!data.ReadLabelSource(&out->label_source))
+    return false;
+
   return true;
 }
 
diff --git a/components/autofill/content/common/autofill_types_struct_traits.h b/components/autofill/content/common/autofill_types_struct_traits.h
index bcdd9bf..88de915 100644
--- a/components/autofill/content/common/autofill_types_struct_traits.h
+++ b/components/autofill/content/common/autofill_types_struct_traits.h
@@ -107,6 +107,15 @@
 };
 
 template <>
+struct EnumTraits<autofill::mojom::LabelSource,
+                  autofill::FormFieldData::LabelSource> {
+  static autofill::mojom::LabelSource ToMojom(
+      autofill::FormFieldData::LabelSource input);
+  static bool FromMojom(autofill::mojom::LabelSource input,
+                        autofill::FormFieldData::LabelSource* output);
+};
+
+template <>
 struct StructTraits<autofill::mojom::FormFieldDataDataView,
                     autofill::FormFieldData> {
   static const base::string16& label(const autofill::FormFieldData& r) {
@@ -188,6 +197,11 @@
     return r.option_contents;
   }
 
+  static autofill::FormFieldData::LabelSource label_source(
+      const autofill::FormFieldData& r) {
+    return r.label_source;
+  }
+
   static bool Read(autofill::mojom::FormFieldDataDataView data,
                    autofill::FormFieldData* out);
 };
diff --git a/components/autofill/content/renderer/form_autofill_util.cc b/components/autofill/content/renderer/form_autofill_util.cc
index 49a2d65f8..7b035ec 100644
--- a/components/autofill/content/renderer/form_autofill_util.cc
+++ b/components/autofill/content/renderer/form_autofill_util.cc
@@ -42,6 +42,7 @@
 #include "third_party/WebKit/public/web/WebOptionElement.h"
 #include "third_party/WebKit/public/web/WebSelectElement.h"
 
+using autofill::FormFieldData;
 using blink::WebDocument;
 using blink::WebElement;
 using blink::WebElementCollection;
@@ -262,10 +263,26 @@
   return node_text;
 }
 
+bool IsLabelValid(base::StringPiece16 inferred_label,
+                  const std::vector<base::char16>& stop_words) {
+  // If |inferred_label| has any character other than those in |stop_words|.
+  auto* first_non_stop_word =
+      std::find_if(inferred_label.begin(), inferred_label.end(),
+                   [&stop_words](base::char16 c) {
+                     return !base::ContainsValue(stop_words, c);
+                   });
+  return first_non_stop_word != inferred_label.end();
+}
+
 // Shared function for InferLabelFromPrevious() and InferLabelFromNext().
-base::string16 InferLabelFromSibling(const WebFormControlElement& element,
-                                     bool forward) {
+bool InferLabelFromSibling(const WebFormControlElement& element,
+                           const std::vector<base::char16>& stop_words,
+                           bool forward,
+                           base::string16* label,
+                           FormFieldData::LabelSource* label_source) {
   base::string16 inferred_label;
+  FormFieldData::LabelSource inferred_label_source =
+      FormFieldData::LabelSource::UNKNOWN;
   WebNode sibling = element;
   while (true) {
     sibling = forward ? sibling.NextSibling() : sibling.PreviousSibling();
@@ -294,6 +311,7 @@
       base::string16 value = FindChildText(sibling);
       // A text node's value will be empty if it is for a line break.
       bool add_space = sibling.IsTextNode() && value.empty();
+      inferred_label_source = FormFieldData::LabelSource::COMBINED;
       inferred_label =
           CombineAndCollapseWhitespace(value, inferred_label, add_space);
       continue;
@@ -303,8 +321,10 @@
     // element, consider the label to be complete.
     base::string16 trimmed_label;
     base::TrimWhitespace(inferred_label, base::TRIM_ALL, &trimmed_label);
-    if (!trimmed_label.empty())
+    if (!trimmed_label.empty()) {
+      inferred_label_source = FormFieldData::LabelSource::COMBINED;
       break;
+    }
 
     // <img> and <br> tags often appear between the input element and its
     // label text, so skip over them.
@@ -316,14 +336,24 @@
     // We only expect <p> and <label> tags to contain the full label text.
     CR_DEFINE_STATIC_LOCAL(WebString, kPage, ("p"));
     CR_DEFINE_STATIC_LOCAL(WebString, kLabel, ("label"));
-    if (HasTagName(sibling, kPage) || HasTagName(sibling, kLabel))
+    bool has_label_tag = HasTagName(sibling, kLabel);
+    if (HasTagName(sibling, kPage) || has_label_tag) {
       inferred_label = FindChildText(sibling);
+      inferred_label_source = has_label_tag
+                                  ? FormFieldData::LabelSource::LABEL_TAG
+                                  : FormFieldData::LabelSource::P_TAG;
+    }
 
     break;
   }
 
   base::TrimWhitespace(inferred_label, base::TRIM_ALL, &inferred_label);
-  return inferred_label;
+  if (IsLabelValid(inferred_label, stop_words)) {
+    *label = std::move(inferred_label);
+    *label_source = inferred_label_source;
+    return true;
+  }
+  return false;
 }
 
 // Helper for |InferLabelForElement()| that infers a label, if possible, from
@@ -334,14 +364,22 @@
 // or   <label>Some Text</label> <input ...>
 // or   Some Text <img><input ...>
 // or   <b>Some Text</b><br/> <input ...>.
-base::string16 InferLabelFromPrevious(const WebFormControlElement& element) {
-  return InferLabelFromSibling(element, false /* forward? */);
+bool InferLabelFromPrevious(const WebFormControlElement& element,
+                            const std::vector<base::char16>& stop_words,
+                            base::string16* label,
+                            FormFieldData::LabelSource* label_source) {
+  return InferLabelFromSibling(element, stop_words, false /* forward? */, label,
+                               label_source);
 }
 
 // Same as InferLabelFromPrevious(), but in the other direction.
 // Useful for cases like: <span><input type="checkbox">Label For Checkbox</span>
-base::string16 InferLabelFromNext(const WebFormControlElement& element) {
-  return InferLabelFromSibling(element, true /* forward? */);
+bool InferLabelFromNext(const WebFormControlElement& element,
+                        const std::vector<base::char16>& stop_words,
+                        base::string16* label,
+                        FormFieldData::LabelSource* label_source) {
+  return InferLabelFromSibling(element, stop_words, true /* forward? */, label,
+                               label_source);
 }
 
 // Helper for |InferLabelForElement()| that infers a label, if possible, from
@@ -651,72 +689,75 @@
   return tag_names;
 }
 
-bool IsLabelValid(base::StringPiece16 inferred_label,
-    const std::vector<base::char16>& stop_words) {
-  // If |inferred_label| has any character other than those in |stop_words|.
-  auto* first_non_stop_word =
-      std::find_if(inferred_label.begin(), inferred_label.end(),
-                   [&stop_words](base::char16 c) {
-                     return !base::ContainsValue(stop_words, c);
-                   });
-  return first_non_stop_word != inferred_label.end();
-}
-
 // Infers corresponding label for |element| from surrounding context in the DOM,
 // e.g. the contents of the preceding <p> tag or text element.
-base::string16 InferLabelForElement(const WebFormControlElement& element,
-    const std::vector<base::char16>& stop_words) {
-  base::string16 inferred_label;
-
+bool InferLabelForElement(const WebFormControlElement& element,
+                          const std::vector<base::char16>& stop_words,
+                          base::string16* label,
+                          FormFieldData::LabelSource* label_source) {
   if (IsCheckableElement(ToWebInputElement(&element))) {
-    inferred_label = InferLabelFromNext(element);
-    if (IsLabelValid(inferred_label, stop_words))
-      return inferred_label;
+    if (InferLabelFromNext(element, stop_words, label, label_source))
+      return true;
   }
 
-  inferred_label = InferLabelFromPrevious(element);
-  if (IsLabelValid(inferred_label, stop_words))
-    return inferred_label;
+  if (InferLabelFromPrevious(element, stop_words, label, label_source))
+    return true;
 
   // If we didn't find a label, check for placeholder text.
-  inferred_label = InferLabelFromPlaceholder(element);
-  if (IsLabelValid(inferred_label, stop_words))
-    return inferred_label;
+  base::string16 inferred_label = InferLabelFromPlaceholder(element);
+  if (IsLabelValid(inferred_label, stop_words)) {
+    *label_source = FormFieldData::LabelSource::PLACE_HOLDER;
+    *label = std::move(inferred_label);
+    return true;
+  }
 
   // For all other searches that involve traversing up the tree, the search
   // order is based on which tag is the closest ancestor to |element|.
   std::vector<std::string> tag_names = AncestorTagNames(element);
   std::set<std::string> seen_tag_names;
+  FormFieldData::LabelSource ancestor_label_source =
+      FormFieldData::LabelSource::UNKNOWN;
   for (const std::string& tag_name : tag_names) {
     if (base::ContainsKey(seen_tag_names, tag_name))
       continue;
 
     seen_tag_names.insert(tag_name);
     if (tag_name == "LABEL") {
+      ancestor_label_source = FormFieldData::LabelSource::LABEL_TAG;
       inferred_label = InferLabelFromEnclosingLabel(element);
     } else if (tag_name == "DIV") {
+      ancestor_label_source = FormFieldData::LabelSource::DIV_TABLE;
       inferred_label = InferLabelFromDivTable(element);
     } else if (tag_name == "TD") {
+      ancestor_label_source = FormFieldData::LabelSource::TD_TAG;
       inferred_label = InferLabelFromTableColumn(element);
       if (!IsLabelValid(inferred_label, stop_words))
         inferred_label = InferLabelFromTableRow(element);
     } else if (tag_name == "DD") {
+      ancestor_label_source = FormFieldData::LabelSource::DD_TAG;
       inferred_label = InferLabelFromDefinitionList(element);
     } else if (tag_name == "LI") {
+      ancestor_label_source = FormFieldData::LabelSource::LI_TAG;
       inferred_label = InferLabelFromListItem(element);
     } else if (tag_name == "FIELDSET") {
       break;
     }
 
-    if (IsLabelValid(inferred_label, stop_words))
-      return inferred_label;
+    if (IsLabelValid(inferred_label, stop_words)) {
+      *label_source = ancestor_label_source;
+      *label = std::move(inferred_label);
+      return true;
+    }
   }
 
   // If we didn't find a label, check the value attr used as the placeholder.
   inferred_label = InferLabelFromValueAttr(element);
-  if (IsLabelValid(inferred_label, stop_words))
-    return inferred_label;
-  return base::string16();
+  if (IsLabelValid(inferred_label, stop_words)) {
+    *label_source = FormFieldData::LabelSource::VALUE;
+    *label = std::move(inferred_label);
+    return true;
+  }
+  return false;
 }
 
 // Fills |option_strings| with the values of the <option> elements present in
@@ -1124,8 +1165,9 @@
 
     const WebFormControlElement& control_element = control_elements[i];
     if (form_fields[field_idx]->label.empty()) {
-      form_fields[field_idx]->label = InferLabelForElement(control_element,
-                                                           stop_words);
+      InferLabelForElement(control_element, stop_words,
+                           &(form_fields[field_idx]->label),
+                           &(form_fields[field_idx]->label_source));
     }
     TruncateString(&form_fields[field_idx]->label, kMaxDataLength);
 
@@ -1838,10 +1880,11 @@
   return FindChildTextWithIgnoreList(node, divs_to_skip);
 }
 
-base::string16 InferLabelForElementForTesting(
-    const WebFormControlElement& element,
-    const std::vector<base::char16>& stop_words) {
-  return InferLabelForElement(element, stop_words);
+bool InferLabelForElementForTesting(const WebFormControlElement& element,
+                                    const std::vector<base::char16>& stop_words,
+                                    base::string16* label,
+                                    FormFieldData::LabelSource* label_source) {
+  return InferLabelForElement(element, stop_words, label, label_source);
 }
 
 }  // namespace form_util
diff --git a/components/autofill/content/renderer/form_autofill_util.h b/components/autofill/content/renderer/form_autofill_util.h
index ed772f5..3699b26 100644
--- a/components/autofill/content/renderer/form_autofill_util.h
+++ b/components/autofill/content/renderer/form_autofill_util.h
@@ -283,9 +283,10 @@
 base::string16 FindChildTextWithIgnoreListForTesting(
     const blink::WebNode& node,
     const std::set<blink::WebNode>& divs_to_skip);
-base::string16 InferLabelForElementForTesting(
-    const blink::WebFormControlElement& element,
-    const std::vector<base::char16>& stop_words);
+bool InferLabelForElementForTesting(const blink::WebFormControlElement& element,
+                                    const std::vector<base::char16>& stop_words,
+                                    base::string16* label,
+                                    FormFieldData::LabelSource* label_source);
 
 }  // namespace form_util
 }  // namespace autofill
diff --git a/components/autofill/content/renderer/form_autofill_util_browsertest.cc b/components/autofill/content/renderer/form_autofill_util_browsertest.cc
index 44dffc4b..e75eb68 100644
--- a/components/autofill/content/renderer/form_autofill_util_browsertest.cc
+++ b/components/autofill/content/renderer/form_autofill_util_browsertest.cc
@@ -17,6 +17,7 @@
 #include "third_party/WebKit/public/web/WebLocalFrame.h"
 #include "third_party/WebKit/public/web/WebSelectElement.h"
 
+using autofill::FormFieldData;
 using blink::WebDocument;
 using blink::WebElement;
 using blink::WebFormControlElement;
@@ -27,6 +28,12 @@
 using blink::WebString;
 using blink::WebVector;
 
+struct AutofillFieldLabelSourceCase {
+  const char* description;
+  const char* html;
+  const FormFieldData::LabelSource label_source;
+};
+
 struct AutofillFieldUtilCase {
   const char* description;
   const char* html;
@@ -199,8 +206,59 @@
         target.ToConst<WebFormControlElement>();
     ASSERT_FALSE(form_target.IsNull());
 
-    EXPECT_EQ(base::UTF8ToUTF16(test_case.expected_label),
-              autofill::form_util::InferLabelForElementForTesting(form_target,
-                                                                  stop_words));
+    FormFieldData::LabelSource label_source =
+        FormFieldData::LabelSource::UNKNOWN;
+    base::string16 label;
+    autofill::form_util::InferLabelForElementForTesting(form_target, stop_words,
+                                                        &label, &label_source);
+    EXPECT_EQ(base::UTF8ToUTF16(test_case.expected_label), label);
+  }
+}
+
+TEST_F(FormAutofillUtilsTest, InferLabelSourceTest) {
+  const char kLabelSourceExpectedLabel[] = "label";
+  static const AutofillFieldLabelSourceCase test_cases[] = {
+      {"DIV_TABLE",
+       "<div><div>label</div><div><input id='target'/></div></div>",
+       FormFieldData::LabelSource::DIV_TABLE},
+      {"LABEL_TAG", "<label>label</label><input id='target'/>",
+       FormFieldData::LabelSource::LABEL_TAG},
+      {"COMBINED", "<b>l</b><strong>a</strong>bel<input id='target'/>",
+       FormFieldData::LabelSource::COMBINED},
+      {"P_TAG", "<p><b>l</b><strong>a</strong>bel</p><input id='target'/>",
+       FormFieldData::LabelSource::P_TAG},
+      {"PLACE_HOLDER", "<input id='target' placeholder='label'/>",
+       FormFieldData::LabelSource::PLACE_HOLDER},
+      {"VALUE", "<input id='target' value='label'/>",
+       FormFieldData::LabelSource::VALUE},
+      {"LI_TAG", "<li>label<div><input id='target'/></div></li>",
+       FormFieldData::LabelSource::LI_TAG},
+      {"TD_TAG",
+       "<table><tr><td>label</td><td><input id='target'/></td></tr></table>",
+       FormFieldData::LabelSource::TD_TAG},
+      {"DD_TAG", "<dl><dt>label</dt><dd><input id='target'></dd></dl>",
+       FormFieldData::LabelSource::DD_TAG},
+  };
+  std::vector<base::char16> stop_words;
+  stop_words.push_back(static_cast<base::char16>('-'));
+
+  for (auto test_case : test_cases) {
+    SCOPED_TRACE(test_case.description);
+    LoadHTML(test_case.html);
+    WebLocalFrame* web_frame = GetMainFrame();
+    ASSERT_NE(nullptr, web_frame);
+    WebElement target = web_frame->GetDocument().GetElementById("target");
+    ASSERT_FALSE(target.IsNull());
+    const WebFormControlElement form_target =
+        target.ToConst<WebFormControlElement>();
+    ASSERT_FALSE(form_target.IsNull());
+
+    FormFieldData::LabelSource label_source =
+        FormFieldData::LabelSource::UNKNOWN;
+    base::string16 label;
+    EXPECT_TRUE(autofill::form_util::InferLabelForElementForTesting(
+        form_target, stop_words, &label, &label_source));
+    EXPECT_EQ(base::UTF8ToUTF16(kLabelSourceExpectedLabel), label);
+    EXPECT_EQ(test_case.label_source, label_source);
   }
 }
diff --git a/components/autofill/core/browser/autofill_experiments.cc b/components/autofill/core/browser/autofill_experiments.cc
index 4ce7fe7..4bc7503 100644
--- a/components/autofill/core/browser/autofill_experiments.cc
+++ b/components/autofill/core/browser/autofill_experiments.cc
@@ -44,6 +44,8 @@
     "AutofillDeleteDisusedCreditCards", base::FEATURE_DISABLED_BY_DEFAULT};
 const base::Feature kAutofillExpandedPopupViews{
     "AutofillExpandedPopupViews", base::FEATURE_DISABLED_BY_DEFAULT};
+const base::Feature kAutofillPreferServerNamePredictions{
+    "AutofillPreferServerNamePredictions", base::FEATURE_DISABLED_BY_DEFAULT};
 const base::Feature kAutofillRationalizeFieldTypePredictions{
     "AutofillRationalizeFieldTypePredictions",
     base::FEATURE_ENABLED_BY_DEFAULT};
diff --git a/components/autofill/core/browser/autofill_experiments.h b/components/autofill/core/browser/autofill_experiments.h
index aa97a20..4065ca1 100644
--- a/components/autofill/core/browser/autofill_experiments.h
+++ b/components/autofill/core/browser/autofill_experiments.h
@@ -35,6 +35,7 @@
 extern const base::Feature kAutofillDeleteDisusedAddresses;
 extern const base::Feature kAutofillDeleteDisusedCreditCards;
 extern const base::Feature kAutofillExpandedPopupViews;
+extern const base::Feature kAutofillPreferServerNamePredictions;
 extern const base::Feature kAutofillRationalizeFieldTypePredictions;
 extern const base::Feature kAutofillSendBillingCustomerNumber;
 extern const base::Feature kAutofillSuppressDisusedAddresses;
diff --git a/components/autofill/core/browser/autofill_field.cc b/components/autofill/core/browser/autofill_field.cc
index 0e86da83..40b92446 100644
--- a/components/autofill/core/browser/autofill_field.cc
+++ b/components/autofill/core/browser/autofill_field.cc
@@ -6,7 +6,9 @@
 
 #include <stdint.h>
 
+#include "base/feature_list.h"
 #include "base/strings/string_number_conversions.h"
+#include "components/autofill/core/browser/autofill_experiments.h"
 #include "components/autofill/core/browser/autofill_type.h"
 #include "components/autofill/core/browser/field_types.h"
 #include "components/autofill/core/browser/proto/server.pb.h"
@@ -108,21 +110,29 @@
   }
 
   if (overall_server_type_ != NO_SERVER_DATA) {
-    // See http://crbug.com/429236 for background on why we might not always
-    // believe the server.
-    // TODO(http://crbug.com/589129) investigate how well the server is doing in
-    // regard to credit card predictions.
-    bool believe_server =
-        !(overall_server_type_ == NAME_FULL &&
-          heuristic_type_ == CREDIT_CARD_NAME_FULL) &&
-        !(overall_server_type_ == CREDIT_CARD_NAME_FULL &&
-          heuristic_type_ == NAME_FULL) &&
-        !(overall_server_type_ == NAME_FIRST &&
-          heuristic_type_ == CREDIT_CARD_NAME_FIRST) &&
-        !(overall_server_type_ == NAME_LAST &&
-          heuristic_type_ == CREDIT_CARD_NAME_LAST) &&
-        // CVC is sometimes type="password", which tricks the server.
-        // See http://crbug.com/469007
+    // Sometimes the server and heuristics disagree on whether a name field
+    // should be associated with an address or a credit card. There was a
+    // decision to prefer the heuristics in these cases, but it looks like
+    // it might be better to fix this server-side.
+    // See http://crbug.com/429236 for background.
+    bool believe_server;
+    if (base::FeatureList::IsEnabled(kAutofillPreferServerNamePredictions)) {
+      believe_server = true;
+    } else {
+      believe_server = !(overall_server_type_ == NAME_FULL &&
+                         heuristic_type_ == CREDIT_CARD_NAME_FULL) &&
+                       !(overall_server_type_ == CREDIT_CARD_NAME_FULL &&
+                         heuristic_type_ == NAME_FULL) &&
+                       !(overall_server_type_ == NAME_FIRST &&
+                         heuristic_type_ == CREDIT_CARD_NAME_FIRST) &&
+                       !(overall_server_type_ == NAME_LAST &&
+                         heuristic_type_ == CREDIT_CARD_NAME_LAST);
+    }
+
+    // Either way, retain a preference for the the CVC heuristic over the
+    // server's password predictions (http://crbug.com/469007)
+    believe_server =
+        believe_server &&
         !(AutofillType(overall_server_type_).group() == PASSWORD_FIELD &&
           heuristic_type_ == CREDIT_CARD_VERIFICATION_CODE);
     if (believe_server)
diff --git a/components/autofill/core/common/autofill_features.cc b/components/autofill/core/common/autofill_features.cc
index 11bb2f1..3d9e3c4 100644
--- a/components/autofill/core/common/autofill_features.cc
+++ b/components/autofill/core/common/autofill_features.cc
@@ -47,6 +47,11 @@
 const base::Feature kAutofillShowTypePredictions{
     "AutofillShowTypePredictions", base::FEATURE_DISABLED_BY_DEFAULT};
 
+// Controls whether inferred label is considered for comparing in
+// FormFieldData.SimilarFieldAs.
+const base::Feature kAutofillSkipComparingInferredLabels{
+    "AutofillSkipComparingInferredLabels", base::FEATURE_DISABLED_BY_DEFAULT};
+
 // Controls whether the credit card upload bubble shows the Google Pay logo and
 // a shorter "Save card?" header message.
 const base::Feature kAutofillUpstreamUseGooglePayBranding{
diff --git a/components/autofill/core/common/autofill_features.h b/components/autofill/core/common/autofill_features.h
index 9dbbdac..5aa1c80c 100644
--- a/components/autofill/core/common/autofill_features.h
+++ b/components/autofill/core/common/autofill_features.h
@@ -18,6 +18,7 @@
 extern const base::Feature kAutofillEnforceMinRequiredFieldsForUpload;
 extern const base::Feature kAutofillRestrictUnownedFieldsToFormlessCheckout;
 extern const base::Feature kAutofillShowTypePredictions;
+extern const base::Feature kAutofillSkipComparingInferredLabels;
 extern const base::Feature kAutofillUpstreamUseGooglePayBranding;
 extern const base::Feature kAutofillUseNewSettingsNameInDropdown;
 
diff --git a/components/autofill/core/common/form_field_data.cc b/components/autofill/core/common/form_field_data.cc
index b26f3f9..4b046908 100644
--- a/components/autofill/core/common/form_field_data.cc
+++ b/components/autofill/core/common/form_field_data.cc
@@ -7,6 +7,7 @@
 #include "base/pickle.h"
 #include "base/strings/string_util.h"
 #include "base/strings/utf_string_conversions.h"
+#include "components/autofill/core/common/autofill_features.h"
 #include "components/autofill/core/common/autofill_util.h"
 
 namespace autofill {
@@ -119,6 +120,21 @@
   return iter->ReadString16(&field_data->id);
 }
 
+bool HaveSameLabel(const FormFieldData& field1, const FormFieldData& field2) {
+  if (field1.label == field2.label &&
+      field1.label_source == field2.label_source) {
+    return true;
+  }
+  // Assume the labels same if they come from same source but not LABEL tag
+  // when kAutofillSkipComparingInferredLabels is enabled.
+  if (base::FeatureList::IsEnabled(
+          features::kAutofillSkipComparingInferredLabels)) {
+    return field1.label_source == field2.label_source &&
+           field1.label_source != FormFieldData::LABEL_TAG;
+  }
+  return false;
+}
+
 }  // namespace
 
 FormFieldData::FormFieldData()
@@ -129,7 +145,8 @@
       should_autocomplete(true),
       role(ROLE_ATTRIBUTE_OTHER),
       text_direction(base::i18n::UNKNOWN_DIRECTION),
-      properties_mask(0) {}
+      properties_mask(0),
+      label_source(LabelSource::UNKNOWN) {}
 
 FormFieldData::FormFieldData(const FormFieldData& other) = default;
 
@@ -138,7 +155,7 @@
 bool FormFieldData::SameFieldAs(const FormFieldData& field) const {
   // A FormFieldData stores a value, but the value is not part of the identity
   // of the field, so we don't want to compare the values.
-  return label == field.label && name == field.name && id == field.id &&
+  return name == field.name && id == field.id &&
          form_control_type == field.form_control_type &&
          autocomplete_attribute == field.autocomplete_attribute &&
          placeholder == field.placeholder && max_length == field.max_length &&
@@ -148,7 +165,8 @@
          IsCheckable(check_status) == IsCheckable(field.check_status) &&
          is_focusable == field.is_focusable &&
          should_autocomplete == field.should_autocomplete &&
-         role == field.role && text_direction == field.text_direction;
+         role == field.role && text_direction == field.text_direction &&
+         HaveSameLabel(*this, field);
   // The option values/contents which are the list of items in the list
   // of a drop-down are currently not considered part of the identity of
   // a form element. This is debatable, since one might base heuristics
@@ -159,7 +177,7 @@
 }
 
 bool FormFieldData::SimilarFieldAs(const FormFieldData& field) const {
-  return label == field.label && name == field.name && id == field.id &&
+  return HaveSameLabel(*this, field) && name == field.name && id == field.id &&
          form_control_type == field.form_control_type &&
          IsCheckable(check_status) == IsCheckable(field.check_status);
 }
@@ -236,6 +254,10 @@
     return true;
   if (text_direction > field.text_direction)
     return false;
+  if (label_source < field.label_source)
+    return true;
+  if (label_source > field.label_source)
+    return false;
   // See SameFieldAs above for why we don't check option_values/contents.
   return false;
 }
@@ -414,7 +436,8 @@
             << "should_autocomplete=" << field.should_autocomplete << " "
             << "role=" << role_str << " "
             << "text_direction=" << field.text_direction << " "
-            << "properties_mask=" << field.properties_mask;
+            << "properties_mask=" << field.properties_mask << " "
+            << "label_source=" << field.label_source;
 }
 
 }  // namespace autofill
diff --git a/components/autofill/core/common/form_field_data.h b/components/autofill/core/common/form_field_data.h
index 1466bed..5273724 100644
--- a/components/autofill/core/common/form_field_data.h
+++ b/components/autofill/core/common/form_field_data.h
@@ -49,6 +49,20 @@
     CHECKED,
   };
 
+  // From which source the label is inferred.
+  enum LabelSource {
+    UNKNOWN,  // The source is unknown.
+    LABEL_TAG,
+    P_TAG,
+    DIV_TABLE,
+    TD_TAG,
+    DD_TAG,
+    LI_TAG,
+    PLACE_HOLDER,
+    COMBINED,  // Combined with various elements.
+    VALUE,     // label is the value of element.
+  };
+
   FormFieldData();
   FormFieldData(const FormFieldData& other);
   ~FormFieldData();
@@ -99,6 +113,10 @@
   // value is "US" and the contents are "United States".
   std::vector<base::string16> option_values;
   std::vector<base::string16> option_contents;
+
+  // Password Manager doesn't use labels nor client side nor server side, so
+  // label_source isn't in serialize methods.
+  LabelSource label_source;
 };
 
 // Serialize and deserialize FormFieldData. These are used when FormData objects
diff --git a/components/cronet/android/cronet_url_request_context_adapter.cc b/components/cronet/android/cronet_url_request_context_adapter.cc
index 1a1e21c..77085e74 100644
--- a/components/cronet/android/cronet_url_request_context_adapter.cc
+++ b/components/cronet/android/cronet_url_request_context_adapter.cc
@@ -52,7 +52,6 @@
 #include "net/http/http_auth_handler_factory.h"
 #include "net/log/file_net_log_observer.h"
 #include "net/log/net_log_util.h"
-#include "net/nqe/external_estimate_provider.h"
 #include "net/nqe/network_quality_estimator_params.h"
 #include "net/proxy_resolution/proxy_config_service_android.h"
 #include "net/proxy_resolution/proxy_service.h"
diff --git a/components/cronet/cronet_url_request_context.cc b/components/cronet/cronet_url_request_context.cc
index 75cbda51..7889cb1 100644
--- a/components/cronet/cronet_url_request_context.cc
+++ b/components/cronet/cronet_url_request_context.cc
@@ -47,7 +47,6 @@
 #include "net/http/http_auth_handler_factory.h"
 #include "net/log/file_net_log_observer.h"
 #include "net/log/net_log_util.h"
-#include "net/nqe/external_estimate_provider.h"
 #include "net/nqe/network_quality_estimator_params.h"
 #include "net/proxy_resolution/proxy_service.h"
 #include "net/quic/core/quic_versions.h"
@@ -339,8 +338,7 @@
     }
 
     network_quality_estimator_ = std::make_unique<net::NetworkQualityEstimator>(
-        std::unique_ptr<net::ExternalEstimateProvider>(), std::move(nqe_params),
-        g_net_log.Get().net_log());
+        std::move(nqe_params), g_net_log.Get().net_log());
     network_quality_estimator_->AddEffectiveConnectionTypeObserver(this);
     network_quality_estimator_->AddRTTAndThroughputEstimatesObserver(this);
 
diff --git a/components/data_reduction_proxy/core/browser/data_reduction_proxy_config_service_client_unittest.cc b/components/data_reduction_proxy/core/browser/data_reduction_proxy_config_service_client_unittest.cc
index 6d2dc59..61108dd 100644
--- a/components/data_reduction_proxy/core/browser/data_reduction_proxy_config_service_client_unittest.cc
+++ b/components/data_reduction_proxy/core/browser/data_reduction_proxy_config_service_client_unittest.cc
@@ -548,12 +548,12 @@
   Init(true);
   AddMockSuccess();
   SetDataReductionProxyEnabled(true, true);
-  EXPECT_FALSE(configurator()->GetProxyConfig().is_valid());
+  EXPECT_TRUE(configurator()->GetProxyConfig().proxy_rules().empty());
   EXPECT_EQ(std::vector<net::ProxyServer>(), GetConfiguredProxiesForHttp());
   config_client()->RetrieveConfig();
   RunUntilIdle();
   VerifyRemoteSuccess(true);
-  EXPECT_TRUE(configurator()->GetProxyConfig().is_valid());
+  EXPECT_FALSE(configurator()->GetProxyConfig().proxy_rules().empty());
 #if defined(OS_ANDROID)
   EXPECT_FALSE(config_client()->foreground_fetch_pending());
 #endif
@@ -790,14 +790,14 @@
   AddMockPreviousSuccess();
 
   SetDataReductionProxyEnabled(true, true);
-  EXPECT_FALSE(configurator()->GetProxyConfig().is_valid());
+  EXPECT_TRUE(configurator()->GetProxyConfig().proxy_rules().empty());
   histogram_tester.ExpectTotalCount(
       "DataReductionProxy.ConfigService.AuthExpired", 0);
   config_client()->RetrieveConfig();
   RunUntilIdle();
   // First remote config should be fetched.
   VerifyRemoteSuccessWithOldConfig();
-  EXPECT_TRUE(configurator()->GetProxyConfig().is_valid());
+  EXPECT_FALSE(configurator()->GetProxyConfig().proxy_rules().empty());
   EXPECT_EQ(kOldSuccessSessionKey, request_options()->GetSecureSession());
   EXPECT_EQ(0, config_client()->GetBackoffErrorCount());
   histogram_tester.ExpectUniqueSample(
@@ -817,7 +817,7 @@
   EXPECT_TRUE(config_client()->ShouldRetryDueToAuthFailure(
       request_headers, parsed.get(), origin, load_timing_info));
   EXPECT_EQ(1, config_client()->GetBackoffErrorCount());
-  EXPECT_FALSE(configurator()->GetProxyConfig().is_valid());
+  EXPECT_TRUE(configurator()->GetProxyConfig().proxy_rules().empty());
 
   // Persisted config on pref should be cleared.
   EXPECT_TRUE(persisted_config().empty());
@@ -947,14 +947,14 @@
   AddMockSuccess();
 
   SetDataReductionProxyEnabled(true, true);
-  EXPECT_FALSE(configurator()->GetProxyConfig().is_valid());
+  EXPECT_TRUE(configurator()->GetProxyConfig().proxy_rules().empty());
   histogram_tester.ExpectTotalCount(
       "DataReductionProxy.ConfigService.AuthExpired", 0);
   config_client()->RetrieveConfig();
   RunUntilIdle();
   // First remote config should be fetched.
   VerifyRemoteSuccessWithOldConfig();
-  EXPECT_TRUE(configurator()->GetProxyConfig().is_valid());
+  EXPECT_FALSE(configurator()->GetProxyConfig().proxy_rules().empty());
   EXPECT_EQ(kOldSuccessSessionKey, request_options()->GetSecureSession());
   EXPECT_EQ(0, config_client()->GetBackoffErrorCount());
   histogram_tester.ExpectUniqueSample(
@@ -974,7 +974,7 @@
   EXPECT_TRUE(config_client()->ShouldRetryDueToAuthFailure(
       request_headers, parsed.get(), origin, load_timing_info));
   EXPECT_EQ(1, config_client()->GetBackoffErrorCount());
-  EXPECT_FALSE(configurator()->GetProxyConfig().is_valid());
+  EXPECT_TRUE(configurator()->GetProxyConfig().proxy_rules().empty());
 
   // Persisted config on pref should be cleared.
   EXPECT_TRUE(persisted_config().empty());
@@ -1046,7 +1046,7 @@
   AddMockSuccess();
 
   SetDataReductionProxyEnabled(true, true);
-  EXPECT_FALSE(configurator()->GetProxyConfig().is_valid());
+  EXPECT_TRUE(configurator()->GetProxyConfig().proxy_rules().empty());
   histogram_tester.ExpectTotalCount(
       "DataReductionProxy.ConfigService.AuthExpired", 0);
   config_client()->RetrieveConfig();
@@ -1054,7 +1054,7 @@
   RunUntilIdle();
   // First remote config should be fetched.
   VerifyRemoteSuccessWithOldConfig();
-  EXPECT_TRUE(configurator()->GetProxyConfig().is_valid());
+  EXPECT_FALSE(configurator()->GetProxyConfig().proxy_rules().empty());
   EXPECT_EQ(kOldSuccessSessionKey, request_options()->GetSecureSession());
   EXPECT_EQ(0, config_client()->GetBackoffErrorCount());
   histogram_tester.ExpectUniqueSample(
@@ -1065,7 +1065,7 @@
   AddMockPreviousSuccess();
 
   SetDataReductionProxyEnabled(true, true);
-  EXPECT_TRUE(configurator()->GetProxyConfig().is_valid());
+  EXPECT_FALSE(configurator()->GetProxyConfig().proxy_rules().empty());
   config_client()->RetrieveConfig();
 
   // Trigger an auth failure.
@@ -1082,7 +1082,7 @@
   EXPECT_TRUE(config_client()->ShouldRetryDueToAuthFailure(
       request_headers, parsed.get(), origin, load_timing_info));
   EXPECT_EQ(1, config_client()->GetBackoffErrorCount());
-  EXPECT_FALSE(configurator()->GetProxyConfig().is_valid());
+  EXPECT_TRUE(configurator()->GetProxyConfig().proxy_rules().empty());
 
   // Persisted config on pref should be cleared.
   EXPECT_TRUE(persisted_config().empty());
@@ -1091,7 +1091,7 @@
   histogram_tester.ExpectBucketCount(
       "DataReductionProxy.ConfigService.AuthExpired", true, 1);
 
-  EXPECT_FALSE(configurator()->GetProxyConfig().is_valid());
+  EXPECT_TRUE(configurator()->GetProxyConfig().proxy_rules().empty());
   // Persisted config on pref should be cleared.
   EXPECT_TRUE(persisted_config().empty());
 
@@ -1099,7 +1099,7 @@
   RunUntilIdle();
   VerifyRemoteSuccess(true);
 
-  EXPECT_TRUE(configurator()->GetProxyConfig().is_valid());
+  EXPECT_FALSE(configurator()->GetProxyConfig().proxy_rules().empty());
   // Persisted config on pref should be cleared.
   EXPECT_FALSE(persisted_config().empty());
   EXPECT_FALSE(persisted_config_retrieval_time().is_null());
diff --git a/components/data_reduction_proxy/core/browser/data_reduction_proxy_config_unittest.cc b/components/data_reduction_proxy/core/browser/data_reduction_proxy_config_unittest.cc
index b486007..8600ee5d 100644
--- a/components/data_reduction_proxy/core/browser/data_reduction_proxy_config_unittest.cc
+++ b/components/data_reduction_proxy/core/browser/data_reduction_proxy_config_unittest.cc
@@ -51,7 +51,6 @@
 #include "net/http/http_status_code.h"
 #include "net/log/test_net_log.h"
 #include "net/nqe/effective_connection_type.h"
-#include "net/nqe/external_estimate_provider.h"
 #include "net/nqe/network_quality_estimator_test_util.h"
 #include "net/test/embedded_test_server/embedded_test_server.h"
 #include "net/traffic_annotation/network_traffic_annotation_test_helper.h"
diff --git a/components/data_reduction_proxy/core/browser/data_reduction_proxy_configurator.cc b/components/data_reduction_proxy/core/browser/data_reduction_proxy_configurator.cc
index cdac351..93cd04c2 100644
--- a/components/data_reduction_proxy/core/browser/data_reduction_proxy_configurator.cc
+++ b/components/data_reduction_proxy/core/browser/data_reduction_proxy_configurator.cc
@@ -51,7 +51,7 @@
   DCHECK(thread_checker_.CalledOnValidThread());
 
   net::ProxyConfig config;
-  DCHECK(!config.is_valid() && config.proxy_rules().proxies_for_http.IsEmpty());
+  DCHECK(config.proxy_rules().proxies_for_http.IsEmpty());
   config.proxy_rules().type =
       net::ProxyConfig::ProxyRules::Type::PROXY_LIST_PER_SCHEME;
 
@@ -97,16 +97,11 @@
   }
 
   if (config.proxy_rules().proxies_for_http.IsEmpty()) {
-    // Return an invalid net config so that data reduction proxy is not used.
-    return config;
+    // Return a DIRECT net config so that data reduction proxy is not used.
+    return net::ProxyConfig::CreateDirect();
   }
 
   config.proxy_rules().bypass_rules = bypass_rules_;
-  // The ID is set to a bogus value. It cannot be left uninitialized, else the
-  // config will return invalid.
-  net::ProxyConfig::ID unused_id = 1;
-  config.set_id(unused_id);
-
   return config;
 }
 
diff --git a/components/data_reduction_proxy/core/browser/data_reduction_proxy_configurator.h b/components/data_reduction_proxy/core/browser/data_reduction_proxy_configurator.h
index 3a8ea5d..7a1a8dd 100644
--- a/components/data_reduction_proxy/core/browser/data_reduction_proxy_configurator.h
+++ b/components/data_reduction_proxy/core/browser/data_reduction_proxy_configurator.h
@@ -71,8 +71,8 @@
   net::ProxyBypassRules bypass_rules_;
 
   // The Data Reduction Proxy's configuration. This contains the list of
-  // acceptable data reduction proxies and bypass rules. It should be accessed
-  // only on the IO thread.
+  // acceptable data reduction proxies and bypass rules, or DIRECT if DRP is not
+  // enabled. It should be accessed only on the IO thread.
   net::ProxyConfig config_;
 
   // Used for logging of network- and Data Reduction Proxy-related events.
diff --git a/components/data_reduction_proxy/core/browser/data_reduction_proxy_configurator_unittest.cc b/components/data_reduction_proxy/core/browser/data_reduction_proxy_configurator_unittest.cc
index bbd82e991..a0291f6 100644
--- a/components/data_reduction_proxy/core/browser/data_reduction_proxy_configurator_unittest.cc
+++ b/components/data_reduction_proxy/core/browser/data_reduction_proxy_configurator_unittest.cc
@@ -221,7 +221,7 @@
   config_->Enable(*manager_,
                   BuildProxyList("https://www.foo.com:443", ProxyServer::CORE,
                                  "http://www.bar.com:80", ProxyServer::CORE));
-  CheckProxyConfig(net::ProxyConfig::ProxyRules::Type::PROXY_LIST_PER_SCHEME, "",
+  CheckProxyConfig(net::ProxyConfig::ProxyRules::Type::EMPTY, "",
                    std::string());
 }
 
@@ -267,7 +267,7 @@
                         "HTTPS www.foo.com:443;PROXY www.bar.com:80;DIRECT",
                         std::string());
   CheckProbeProxyConfig(http_proxies, false /* probe_url_config */,
-                        net::ProxyConfig::ProxyRules::Type::PROXY_LIST_PER_SCHEME, "",
+                        net::ProxyConfig::ProxyRules::Type::EMPTY, "",
                         std::string());
 }
 
diff --git a/components/data_reduction_proxy/core/browser/data_reduction_proxy_delegate_unittest.cc b/components/data_reduction_proxy/core/browser/data_reduction_proxy_delegate_unittest.cc
index f5ccbb5..2b6444db 100644
--- a/components/data_reduction_proxy/core/browser/data_reduction_proxy_delegate_unittest.cc
+++ b/components/data_reduction_proxy/core/browser/data_reduction_proxy_delegate_unittest.cc
@@ -327,21 +327,16 @@
   // afterwards.
   // Another proxy is used. It should be used afterwards.
   result.Use(direct_proxy_info);
-  net::ProxyConfig::ID prev_id = result.config_id();
   proxy_delegate()->OnResolveProxy(url, "GET", empty_proxy_retry_info, &result);
   EXPECT_EQ(params()->proxies_for_http().front().proxy_server(),
             result.proxy_server());
-  // Only the proxy list should be updated, not the proxy info.
-  EXPECT_EQ(result.config_id(), prev_id);
 
   // A direct connection is used, but the data reduction proxy is on the retry
   // list. A direct connection should be used afterwards.
   result.Use(direct_proxy_info);
-  prev_id = result.config_id();
   proxy_delegate()->OnResolveProxy(GURL("ws://echo.websocket.org/"), "GET",
                                    data_reduction_proxy_retry_info, &result);
   EXPECT_TRUE(result.proxy_server().is_direct());
-  EXPECT_EQ(result.config_id(), prev_id);
 
   // Test that ws:// and wss:// URLs bypass the data reduction proxy.
   result.UseDirect();
@@ -422,7 +417,6 @@
     // A direct connection is used. The data reduction proxy should be used
     // afterwards.
     result.Use(direct_proxy_info);
-    net::ProxyConfig::ID prev_id = result.config_id();
     proxy_delegate()->OnResolveProxy(url, "GET", empty_proxy_retry_info,
                                      &result);
     //
@@ -432,8 +426,6 @@
     } else {
       EXPECT_TRUE(result.proxy_server().is_direct());
     }
-    // Only the proxy list should be updated, not the proxy info.
-    EXPECT_EQ(result.config_id(), prev_id);
   }
 }
 
diff --git a/components/data_reduction_proxy/core/browser/data_reduction_proxy_util.cc b/components/data_reduction_proxy/core/browser/data_reduction_proxy_util.cc
index 522aa31..d4010a64 100644
--- a/components/data_reduction_proxy/core/browser/data_reduction_proxy_util.cc
+++ b/components/data_reduction_proxy/core/browser/data_reduction_proxy_util.cc
@@ -181,7 +181,7 @@
                                  const GURL& url,
                                  net::ProxyInfo* data_reduction_proxy_info) {
   DCHECK(data_reduction_proxy_info);
-  if (!proxy_config.is_valid())
+  if (proxy_config.proxy_rules().empty())
     return false;
   proxy_config.proxy_rules().Apply(url, data_reduction_proxy_info);
   data_reduction_proxy_info->DeprioritizeBadProxies(proxy_retry_info);
diff --git a/components/data_reduction_proxy/core/browser/data_reduction_proxy_util.h b/components/data_reduction_proxy/core/browser/data_reduction_proxy_util.h
index c9f6090..98f4f52 100644
--- a/components/data_reduction_proxy/core/browser/data_reduction_proxy_util.h
+++ b/components/data_reduction_proxy/core/browser/data_reduction_proxy_util.h
@@ -88,10 +88,11 @@
                                    const std::string& method);
 
 // Determines if |proxy_config| would override a direct. |proxy_config| should
-// be a data reduction proxy config with proxy servers mapped in the rules.
-// |proxy_retry_info| contains the list of bad proxies. |url| is used to
-// determine whether it is HTTP or HTTPS. |data_reduction_proxy_info| is an out
-// param that will contain the proxies that should be used.
+// be a data reduction proxy config with proxy servers mapped in the
+// rules, or DIRECT to indicate DRP is not to be used. |proxy_retry_info|
+// contains the list of bad proxies. |url| is used to determine whether it is
+// HTTP or HTTPS. |data_reduction_proxy_info| is an out param that will contain
+// the proxies that should be used.
 bool ApplyProxyConfigToProxyInfo(const net::ProxyConfig& proxy_config,
                                  const net::ProxyRetryInfoMap& proxy_retry_info,
                                  const GURL& url,
diff --git a/components/nacl/loader/BUILD.gn b/components/nacl/loader/BUILD.gn
index 4e523ae..4ebb4e9 100644
--- a/components/nacl/loader/BUILD.gn
+++ b/components/nacl/loader/BUILD.gn
@@ -147,9 +147,9 @@
       # we can enable ThinLTO and CFI.
       if (use_thin_lto) {
         if (use_lld) {
-          ldflags = [ "-Wl,-mllvm,-compute-dead=false" ]
+          ldflags += [ "-Wl,-mllvm,-compute-dead=false" ]
         } else {
-          ldflags = [ "-Wl,-plugin-opt,-compute-dead=false" ]
+          ldflags += [ "-Wl,-plugin-opt,-compute-dead=false" ]
         }
       }
     }
diff --git a/components/omnibox/browser/BUILD.gn b/components/omnibox/browser/BUILD.gn
index fd0a96a..4629abb 100644
--- a/components/omnibox/browser/BUILD.gn
+++ b/components/omnibox/browser/BUILD.gn
@@ -172,7 +172,6 @@
     "//components/search",
     "//components/search_engines",
     "//components/sessions",
-    "//components/signin/core/browser",
     "//components/strings",
     "//components/sync",
     "//components/toolbar",
diff --git a/components/omnibox/browser/DEPS b/components/omnibox/browser/DEPS
index 4e2e8af..e3df928a 100644
--- a/components/omnibox/browser/DEPS
+++ b/components/omnibox/browser/DEPS
@@ -2,7 +2,6 @@
   "+components/bookmarks/browser",
   "+components/bookmarks/test",
   "+components/data_use_measurement/core",
-  "+components/grit/components_scaled_resources.h",
   "+components/history/core/browser",
   "+components/history/core/test",
   "+components/keyed_service/core",
@@ -17,7 +16,6 @@
   "+components/search_engines",
   "+components/sessions",
   "+components/sync",
-  "+components/signin/core/browser",
   "+components/strings/grit/components_strings.h",
   "+components/toolbar",
   "+components/url_formatter",
diff --git a/components/omnibox/browser/bookmark_provider.cc b/components/omnibox/browser/bookmark_provider.cc
index 3988a03..b9035d4 100644
--- a/components/omnibox/browser/bookmark_provider.cc
+++ b/components/omnibox/browser/bookmark_provider.cc
@@ -18,6 +18,7 @@
 #include "components/omnibox/browser/autocomplete_result.h"
 #include "components/omnibox/browser/titled_url_match_utils.h"
 #include "components/prefs/pref_service.h"
+#include "components/query_parser/snippet.h"
 #include "third_party/metrics_proto/omnibox_input_type.pb.h"
 #include "url/url_constants.h"
 
diff --git a/components/omnibox/browser/omnibox_view.cc b/components/omnibox/browser/omnibox_view.cc
index 91eebd8..d297c63 100644
--- a/components/omnibox/browser/omnibox_view.cc
+++ b/components/omnibox/browser/omnibox_view.cc
@@ -14,7 +14,6 @@
 #include "base/strings/string_util.h"
 #include "base/strings/utf_string_conversions.h"
 #include "build/build_config.h"
-#include "components/grit/components_scaled_resources.h"
 #include "components/omnibox/browser/autocomplete_match.h"
 #include "components/omnibox/browser/omnibox_edit_controller.h"
 #include "components/omnibox/browser/omnibox_edit_model.h"
diff --git a/components/omnibox/browser/physical_web_provider.cc b/components/omnibox/browser/physical_web_provider.cc
index f0fc9c3..ec07a363 100644
--- a/components/omnibox/browser/physical_web_provider.cc
+++ b/components/omnibox/browser/physical_web_provider.cc
@@ -23,6 +23,7 @@
 #include "components/omnibox/browser/url_prefix.h"
 #include "components/omnibox/browser/verbatim_match.h"
 #include "components/physical_web/data_source/physical_web_data_source.h"
+#include "components/query_parser/query_parser.h"
 #include "components/strings/grit/components_strings.h"
 #include "components/url_formatter/url_formatter.h"
 #include "ui/base/l10n/l10n_util.h"
diff --git a/components/onc/docs/onc_spec.md b/components/onc/docs/onc_spec.md
index 46a3d77..bb3877a 100644
--- a/components/onc/docs/onc_spec.md
+++ b/components/onc/docs/onc_spec.md
@@ -1760,6 +1760,18 @@
 
 * "X${LOGIN_ID}" -> "Xbobquail"
 
+
+## String Substitutions
+The value of **WiFi.EAP.Password** is subject to string substitution. These
+differ from the **String Expansions** section above in that an exact match of
+the substitution variable is required in order to substitute the real value.
+
+### Example expansions, assuming the user password was *helloworld*:
+
+* "${PASSWORD}" -> "helloworld"
+
+* "${PASSWORD}foo" -> "${PASSWORD}foo"
+
 ## Detection
 
 This format should be sent in files ending in the .onc extension. When
diff --git a/components/password_manager/content/common/credential_manager_mojom_traits.cc b/components/password_manager/content/common/credential_manager_mojom_traits.cc
index 0fb338c..e0e3a3f 100644
--- a/components/password_manager/content/common/credential_manager_mojom_traits.cc
+++ b/components/password_manager/content/common/credential_manager_mojom_traits.cc
@@ -87,7 +87,6 @@
           password_manager::CredentialManagerError::PASSWORDSTOREUNAVAILABLE;
       return true;
     case password_manager::mojom::CredentialManagerError::NOT_ALLOWED:
-    case password_manager::mojom::CredentialManagerError::TIMED_OUT:
     case password_manager::mojom::CredentialManagerError::NOT_SUPPORTED:
     case password_manager::mojom::CredentialManagerError::INVALID_DOMAIN:
     case password_manager::mojom::CredentialManagerError::NOT_IMPLEMENTED:
diff --git a/components/password_manager/core/browser/export/password_manager_exporter.cc b/components/password_manager/core/browser/export/password_manager_exporter.cc
index 687564e..3265add7 100644
--- a/components/password_manager/core/browser/export/password_manager_exporter.cc
+++ b/components/password_manager/core/browser/export/password_manager_exporter.cc
@@ -112,11 +112,7 @@
 
   // If we are currently writing to the disk, we will have to cleanup the file
   // once writing stops.
-  if (!destination_.empty()) {
-    task_runner_->PostTask(FROM_HERE,
-                           base::BindOnce(base::IgnoreResult(delete_function_),
-                                          destination_, false));
-  }
+  Cleanup();
 
   // TODO(crbug.com/789561) If the passwords have already been written to the
   // disk, then we've already recorded ExportPasswordsResult::SUCCESS. Ideally,
@@ -176,6 +172,8 @@
   } else {
     OnProgress(ExportProgressStatus::FAILED_WRITE_FAILED,
                destination_.DirName().BaseName().AsUTF8Unsafe());
+    // Don't leave partial password files, if we tell the user we couldn't write
+    Cleanup();
 
     UMA_HISTOGRAM_ENUMERATION("PasswordManager.ExportPasswordsToCSVResult",
                               ExportPasswordsResult::WRITE_FAILED,
@@ -190,4 +188,18 @@
   on_progress_.Run(status, folder);
 }
 
+void PasswordManagerExporter::Cleanup() {
+  // The PasswordManagerExporter instance may be destroyed before the cleanup is
+  // executed, e.g. because a new export was initiated. The cleanup should be
+  // carried out regardless, so we only schedule tasks which own their
+  // arguments.
+  // TODO(crbug.com/811779) When Chrome is overwriting an existing file, cancel
+  // should restore the file rather than delete it.
+  if (!destination_.empty()) {
+    task_runner_->PostTask(FROM_HERE,
+                           base::BindOnce(base::IgnoreResult(delete_function_),
+                                          destination_, false));
+  }
+}
+
 }  // namespace password_manager
diff --git a/components/password_manager/core/browser/export/password_manager_exporter.h b/components/password_manager/core/browser/export/password_manager_exporter.h
index 3885334..72ba42d 100644
--- a/components/password_manager/core/browser/export/password_manager_exporter.h
+++ b/components/password_manager/core/browser/export/password_manager_exporter.h
@@ -86,6 +86,10 @@
   // it can be provided by GetProgressStatus.
   void OnProgress(ExportProgressStatus status, const std::string& folder);
 
+  // Export failed or was cancelled. Restore the state of the file system by
+  // removing any partial or unwanted files from the filesystem.
+  void Cleanup();
+
   // The source of the password list which will be exported.
   CredentialProviderInterface* const credential_provider_interface_;
 
diff --git a/components/password_manager/core/browser/export/password_manager_exporter_unittest.cc b/components/password_manager/core/browser/export/password_manager_exporter_unittest.cc
index 0976d86a..ba31cc8e 100644
--- a/components/password_manager/core/browser/export/password_manager_exporter_unittest.cc
+++ b/components/password_manager/core/browser/export/password_manager_exporter_unittest.cc
@@ -131,7 +131,7 @@
   DISALLOW_COPY_AND_ASSIGN(PasswordManagerExporterTest);
 };
 
-TEST_F(PasswordManagerExporterTest, SuccessfulExport) {
+TEST_F(PasswordManagerExporterTest, PasswordExportSetPasswordListFirst) {
   std::vector<std::unique_ptr<autofill::PasswordForm>> password_list =
       CreatePasswordList();
   fake_credential_provider_.SetPasswordList(password_list);
@@ -161,6 +161,8 @@
       ExportPasswordsResult::SUCCESS, 1);
 }
 
+// When writing fails, we should notify the UI of the failure and try to cleanup
+// a possibly partial passwords file.
 TEST_F(PasswordManagerExporterTest, WriteFileFailed) {
   std::vector<std::unique_ptr<autofill::PasswordForm>> password_list =
       CreatePasswordList();
@@ -169,6 +171,7 @@
       destination_path_.DirName().BaseName().AsUTF8Unsafe());
 
   EXPECT_CALL(mock_write_file_, Run(_, _, _)).WillOnce(Return(-1));
+  EXPECT_CALL(mock_delete_file_, Run(destination_path_, false));
   EXPECT_CALL(
       mock_on_progress_,
       Run(password_manager::ExportProgressStatus::IN_PROGRESS, IsEmpty()));
@@ -282,8 +285,8 @@
       ExportPasswordsResult::USER_ABORTED, 1);
 }
 
-// The "Cancel" button may still be visible on the UI after Chrome has completed
-// exporting. If the user chooses to cancel, Chrome should clear the file.
+// The "Cancel" button may still be visible on the UI after we've completed
+// exporting. If they choose to cancel, we should clear the file.
 TEST_F(PasswordManagerExporterTest, CancelAfterExporting) {
   std::vector<std::unique_ptr<autofill::PasswordForm>> password_list =
       CreatePasswordList();
diff --git a/components/policy/core/common/cloud/cloud_policy_constants.cc b/components/policy/core/common/cloud/cloud_policy_constants.cc
index 60e8ab9..bddd608 100644
--- a/components/policy/core/common/cloud/cloud_policy_constants.cc
+++ b/components/policy/core/common/cloud/cloud_policy_constants.cc
@@ -55,6 +55,8 @@
     "active_directory_play_activity";
 const char kValueRequestCheckDeviceLicense[] = "check_device_license";
 const char kValueRequestAppInstallReport[] = "app_install_report";
+const char kValueRequestTokenEnrollment[] = "register_browser";
+const char kValueRequestChromeDesktopReport[] = "chrome_desktop_report";
 
 const char kChromeDevicePolicyType[] = "google/chromeos/device";
 #if defined(OS_CHROMEOS)
@@ -70,6 +72,10 @@
 const char kChromeExtensionPolicyType[] = "google/chrome/extension";
 const char kChromeSigninExtensionPolicyType[] =
     "google/chromeos/signinextension";
+const char kChromeMachineLevelUserCloudPolicyType[] =
+    "google/chrome/machine-level-user";
+const char kChromeMachineLevelExtensionCloudPolicyType[] =
+    "google/chrome/machine-level-extension";
 
 }  // namespace dm_protocol
 
diff --git a/components/policy/core/common/cloud/cloud_policy_constants.h b/components/policy/core/common/cloud/cloud_policy_constants.h
index 7bc7e17..f2a82709 100644
--- a/components/policy/core/common/cloud/cloud_policy_constants.h
+++ b/components/policy/core/common/cloud/cloud_policy_constants.h
@@ -47,6 +47,8 @@
 POLICY_EXPORT extern const char kValueRequestActiveDirectoryPlayActivity[];
 POLICY_EXPORT extern const char kValueRequestCheckDeviceLicense[];
 POLICY_EXPORT extern const char kValueRequestAppInstallReport[];
+POLICY_EXPORT extern const char kValueRequestTokenEnrollment[];
+POLICY_EXPORT extern const char kValueRequestChromeDesktopReport[];
 
 // Policy type strings for the policy_type field in PolicyFetchRequest.
 POLICY_EXPORT extern const char kChromeDevicePolicyType[];
@@ -54,6 +56,8 @@
 POLICY_EXPORT extern const char kChromePublicAccountPolicyType[];
 POLICY_EXPORT extern const char kChromeExtensionPolicyType[];
 POLICY_EXPORT extern const char kChromeSigninExtensionPolicyType[];
+POLICY_EXPORT extern const char kChromeMachineLevelUserCloudPolicyType[];
+POLICY_EXPORT extern const char kChromeMachineLevelExtensionCloudPolicyType[];
 
 // These codes are sent in the |error_code| field of PolicyFetchResponse.
 enum PolicyFetchStatus {
diff --git a/components/policy/core/common/cloud/device_management_service.cc b/components/policy/core/common/cloud/device_management_service.cc
index 385ea95..b81384b1 100644
--- a/components/policy/core/common/cloud/device_management_service.cc
+++ b/components/policy/core/common/cloud/device_management_service.cc
@@ -34,6 +34,8 @@
 
 const char kServiceTokenAuthHeader[] = "Authorization: GoogleLogin auth=";
 const char kDMTokenAuthHeader[] = "Authorization: GoogleDMToken token=";
+const char kEnrollmentTokenAuthHeader[] =
+    "Authorization: GoogleEnrollmentToken token=";
 
 // Number of times to retry on ERR_NETWORK_CHANGED errors.
 const int kMaxRetries = 3;
@@ -160,6 +162,10 @@
       return dm_protocol::kValueRequestCheckDeviceLicense;
     case DeviceManagementRequestJob::TYPE_UPLOAD_APP_INSTALL_REPORT:
       return dm_protocol::kValueRequestAppInstallReport;
+    case DeviceManagementRequestJob::TYPE_TOKEN_ENROLLMENT:
+      return dm_protocol::kValueRequestTokenEnrollment;
+    case DeviceManagementRequestJob::TYPE_CHROME_DESKTOP_REPORT:
+      return dm_protocol::kValueRequestChromeDesktopReport;
   }
   NOTREACHED() << "Invalid job type " << type;
   return "";
@@ -412,6 +418,8 @@
     extra_headers += kServiceTokenAuthHeader + gaia_token_ + "\n";
   if (!dm_token_.empty())
     extra_headers += kDMTokenAuthHeader + dm_token_ + "\n";
+  if (!enrollment_token_.empty())
+    extra_headers += kEnrollmentTokenAuthHeader + enrollment_token_ + "\n";
   fetcher->SetExtraRequestHeaders(extra_headers);
 }
 
@@ -485,6 +493,10 @@
   AddParameter(dm_protocol::kParamDeviceID, client_id);
 }
 
+void DeviceManagementRequestJob::SetEnrollmentToken(const std::string& token) {
+  enrollment_token_ = token;
+}
+
 void DeviceManagementRequestJob::SetCritical(bool critical) {
   if (critical)
     AddParameter(dm_protocol::kParamCritical, "true");
diff --git a/components/policy/core/common/cloud/device_management_service.h b/components/policy/core/common/cloud/device_management_service.h
index b6d0b51e..ec6f24b7 100644
--- a/components/policy/core/common/cloud/device_management_service.h
+++ b/components/policy/core/common/cloud/device_management_service.h
@@ -64,6 +64,8 @@
     TYPE_ACTIVE_DIRECTORY_PLAY_ACTIVITY = 15,
     TYPE_REQUEST_LICENSE_TYPES = 16,
     TYPE_UPLOAD_APP_INSTALL_REPORT = 17,
+    TYPE_TOKEN_ENROLLMENT = 18,
+    TYPE_CHROME_DESKTOP_REPORT = 19,
   };
 
   typedef base::Callback<
@@ -80,6 +82,7 @@
   void SetOAuthToken(const std::string& oauth_token);
   void SetDMToken(const std::string& dm_token);
   void SetClientID(const std::string& client_id);
+  void SetEnrollmentToken(const std::string& token);
   // Sets the critical request parameter, which is used to differentiate regular
   // DMServer requests (like scheduled policy fetches) from time-sensitive ones
   // (like policy fetch during device enrollment). Should only be called before
@@ -115,6 +118,7 @@
   ParameterMap query_params_;
   std::string gaia_token_;
   std::string dm_token_;
+  std::string enrollment_token_;
   enterprise_management::DeviceManagementRequest request_;
   RetryCallback retry_callback_;
 
diff --git a/components/policy/core/common/cloud/device_management_service_unittest.cc b/components/policy/core/common/cloud/device_management_service_unittest.cc
index 6f5031dc..9cdd2bf 100644
--- a/components/policy/core/common/cloud/device_management_service_unittest.cc
+++ b/components/policy/core/common/cloud/device_management_service_unittest.cc
@@ -46,6 +46,7 @@
 const char kDMToken[] = "device-management-token";
 const char kClientID[] = "device-id";
 const char kRobotAuthCode[] = "robot-oauth-auth-code";
+const char kEnrollmentToken[] = "enrollment_token";
 
 // Unit tests for the device management policy service. The tests are run
 // against a TestURLFetcherFactory that is used to short-circuit the request
@@ -110,6 +111,20 @@
     return job;
   }
 
+  DeviceManagementRequestJob* StartTokenEnrollmentJob() {
+    DeviceManagementRequestJob* job =
+        service_->CreateJob(DeviceManagementRequestJob::TYPE_TOKEN_ENROLLMENT,
+                            request_context_.get());
+    job->SetEnrollmentToken(kEnrollmentToken);
+    job->SetClientID(kClientID);
+    job->GetRequest()->mutable_register_request();
+    job->SetRetryCallback(base::BindRepeating(
+        &DeviceManagementServiceTestBase::OnJobRetry, base::Unretained(this)));
+    job->Start(base::BindRepeating(&DeviceManagementServiceTestBase::OnJobDone,
+                                   base::Unretained(this)));
+    return job;
+  }
+
   DeviceManagementRequestJob* StartApiAuthCodeFetchJob() {
     DeviceManagementRequestJob* job = service_->CreateJob(
         DeviceManagementRequestJob::TYPE_API_AUTH_CODE_FETCH,
@@ -277,6 +292,18 @@
                GetParam().response_);
 }
 
+TEST_P(DeviceManagementServiceFailedRequestTest, TokenEnrollmentRequest) {
+  EXPECT_CALL(*this, OnJobDone(GetParam().expected_status_, _, _));
+  EXPECT_CALL(*this, OnJobRetry(_)).Times(0);
+  std::unique_ptr<DeviceManagementRequestJob> request_job(
+      StartTokenEnrollmentJob());
+  net::TestURLFetcher* fetcher = GetFetcher();
+  ASSERT_TRUE(fetcher);
+
+  SendResponse(fetcher, GetParam().error_, GetParam().http_status_,
+               GetParam().response_);
+}
+
 TEST_P(DeviceManagementServiceFailedRequestTest, ApiAuthCodeFetchRequest) {
   EXPECT_CALL(*this, OnJobDone(GetParam().expected_status_, _, _));
   EXPECT_CALL(*this, OnJobRetry(_)).Times(0);
@@ -543,6 +570,39 @@
   SendResponse(fetcher, net::OK, 200, response_data);
 }
 
+TEST_F(DeviceManagementServiceTest, TokenEnrollmentRequest) {
+  em::DeviceManagementResponse expected_response;
+  expected_response.mutable_register_response()->set_device_management_token(
+      kDMToken);
+  EXPECT_CALL(
+      *this, OnJobDone(DM_STATUS_SUCCESS, _, MessageEquals(expected_response)));
+  EXPECT_CALL(*this, OnJobRetry(_)).Times(0);
+  std::unique_ptr<DeviceManagementRequestJob> request_job(
+      StartTokenEnrollmentJob());
+  net::TestURLFetcher* fetcher = GetFetcher();
+  ASSERT_TRUE(fetcher);
+
+  CheckURLAndQueryParams(fetcher->GetOriginalURL(),
+                         dm_protocol::kValueRequestTokenEnrollment, kClientID,
+                         "");
+
+  // Make sure request is properly authorized.
+  net::HttpRequestHeaders headers;
+  fetcher->GetExtraRequestHeaders(&headers);
+  std::string header;
+  ASSERT_TRUE(headers.GetHeader("Authorization", &header));
+  EXPECT_EQ("GoogleEnrollmentToken token=enrollment_token", header);
+
+  std::string expected_data;
+  ASSERT_TRUE(request_job->GetRequest()->SerializeToString(&expected_data));
+  EXPECT_EQ(expected_data, fetcher->upload_data());
+
+  // Generate the response.
+  std::string response_data;
+  ASSERT_TRUE(expected_response.SerializeToString(&response_data));
+  SendResponse(fetcher, net::OK, 200, response_data);
+}
+
 TEST_F(DeviceManagementServiceTest, ApiAuthCodeFetchRequest) {
   em::DeviceManagementResponse expected_response;
   expected_response.mutable_service_api_access_response()->set_auth_code(
@@ -650,6 +710,18 @@
   request_job.reset();
 }
 
+TEST_F(DeviceManagementServiceTest, CancelTokenEnrollmentRequest) {
+  EXPECT_CALL(*this, OnJobDone(_, _, _)).Times(0);
+  EXPECT_CALL(*this, OnJobRetry(_)).Times(0);
+  std::unique_ptr<DeviceManagementRequestJob> request_job(
+      StartTokenEnrollmentJob());
+  net::TestURLFetcher* fetcher = GetFetcher();
+  ASSERT_TRUE(fetcher);
+
+  // There shouldn't be any callbacks.
+  request_job.reset();
+}
+
 TEST_F(DeviceManagementServiceTest, CancelApiAuthCodeFetch) {
   EXPECT_CALL(*this, OnJobDone(_, _, _)).Times(0);
   EXPECT_CALL(*this, OnJobRetry(_)).Times(0);
diff --git a/components/policy/core/common/cloud/user_cloud_policy_store.cc b/components/policy/core/common/cloud/user_cloud_policy_store.cc
index bc23452..3bea9fd 100644
--- a/components/policy/core/common/cloud/user_cloud_policy_store.cc
+++ b/components/policy/core/common/cloud/user_cloud_policy_store.cc
@@ -77,9 +77,8 @@
 
 // Loads policy from the backing file. Returns a PolicyLoadResult with the
 // results of the fetch.
-policy::PolicyLoadResult LoadPolicyFromDisk(
-    const base::FilePath& policy_path,
-    const base::FilePath& key_path) {
+policy::PolicyLoadResult LoadPolicyFromDisk(const base::FilePath& policy_path,
+                                            const base::FilePath& key_path) {
   policy::PolicyLoadResult result;
   // If the backing file does not exist, just return. We don't verify the key
   // path here, because the key is optional (the validation code will fail if
@@ -169,34 +168,19 @@
 
 }  // namespace
 
-UserCloudPolicyStore::UserCloudPolicyStore(
+DesktopCloudPolicyStore::DesktopCloudPolicyStore(
     const base::FilePath& policy_path,
     const base::FilePath& key_path,
-    scoped_refptr<base::SequencedTaskRunner> background_task_runner)
-    : UserCloudPolicyStoreBase(background_task_runner),
+    scoped_refptr<base::SequencedTaskRunner> background_task_runner,
+    PolicyScope policy_scope)
+    : UserCloudPolicyStoreBase(background_task_runner, policy_scope),
       policy_path_(policy_path),
       key_path_(key_path),
       weak_factory_(this) {}
 
-UserCloudPolicyStore::~UserCloudPolicyStore() {}
+DesktopCloudPolicyStore::~DesktopCloudPolicyStore() {}
 
-// static
-std::unique_ptr<UserCloudPolicyStore> UserCloudPolicyStore::Create(
-    const base::FilePath& profile_path,
-    scoped_refptr<base::SequencedTaskRunner> background_task_runner) {
-  base::FilePath policy_path =
-      profile_path.Append(kPolicyDir).Append(kPolicyCacheFile);
-  base::FilePath key_path =
-      profile_path.Append(kPolicyDir).Append(kKeyCacheFile);
-  return base::WrapUnique(
-      new UserCloudPolicyStore(policy_path, key_path, background_task_runner));
-}
-
-void UserCloudPolicyStore::SetSigninUsername(const std::string& username) {
-  signin_username_ = username;
-}
-
-void UserCloudPolicyStore::LoadImmediately() {
+void DesktopCloudPolicyStore::LoadImmediately() {
   DVLOG(1) << "Initiating immediate policy load from disk";
   // Cancel any pending Load/Store/Validate operations.
   weak_factory_.InvalidateWeakPtrs();
@@ -206,13 +190,13 @@
   PolicyLoaded(false, result);
 }
 
-void UserCloudPolicyStore::Clear() {
+void DesktopCloudPolicyStore::Clear() {
+  background_task_runner()->PostTask(
+      FROM_HERE, base::BindOnce(base::IgnoreResult(&base::DeleteFile),
+                                policy_path_, false));
   background_task_runner()->PostTask(
       FROM_HERE,
-      base::Bind(base::IgnoreResult(&base::DeleteFile), policy_path_, false));
-  background_task_runner()->PostTask(
-      FROM_HERE,
-      base::Bind(base::IgnoreResult(&base::DeleteFile), key_path_, false));
+      base::BindOnce(base::IgnoreResult(&base::DeleteFile), key_path_, false));
   policy_.reset();
   policy_map_.Clear();
   policy_signature_public_key_.clear();
@@ -220,7 +204,7 @@
   NotifyStoreLoaded();
 }
 
-void UserCloudPolicyStore::Load() {
+void DesktopCloudPolicyStore::Load() {
   DVLOG(1) << "Initiating policy load from disk";
   // Cancel any pending Load/Store/Validate operations.
   weak_factory_.InvalidateWeakPtrs();
@@ -228,19 +212,17 @@
   // Start a new Load operation and have us get called back when it is
   // complete.
   base::PostTaskAndReplyWithResult(
-      background_task_runner().get(),
-      FROM_HERE,
-      base::Bind(&LoadPolicyFromDisk, policy_path_, key_path_),
-      base::Bind(&UserCloudPolicyStore::PolicyLoaded,
-                 weak_factory_.GetWeakPtr(),
-                 true));
+      background_task_runner().get(), FROM_HERE,
+      base::BindOnce(&LoadPolicyFromDisk, policy_path_, key_path_),
+      base::BindOnce(&DesktopCloudPolicyStore::PolicyLoaded,
+                     weak_factory_.GetWeakPtr(), true));
 }
 
-void UserCloudPolicyStore::PolicyLoaded(bool validate_in_background,
-                                        PolicyLoadResult result) {
+void DesktopCloudPolicyStore::PolicyLoaded(bool validate_in_background,
+                                           PolicyLoadResult result) {
+  // TODO(zmin): figure out what do with the metrics. https://crbug.com/814371
   UMA_HISTOGRAM_ENUMERATION("Enterprise.UserCloudPolicyStore.LoadStatus",
-                            result.status,
-                            LOAD_RESULT_SIZE);
+                            result.status, LOAD_RESULT_SIZE);
   switch (result.status) {
     case LOAD_RESULT_LOAD_ERROR:
       status_ = STATUS_LOAD_ERROR;
@@ -272,12 +254,12 @@
         // we've done our first key rotation).
       }
 
-      Validate(
-          std::move(cloud_policy), std::move(key), validate_in_background,
-          base::Bind(&UserCloudPolicyStore::InstallLoadedPolicyAfterValidation,
-                     weak_factory_.GetWeakPtr(), doing_key_rotation,
-                     result.key.has_signing_key() ? result.key.signing_key()
-                                                  : std::string()));
+      Validate(std::move(cloud_policy), std::move(key), validate_in_background,
+               base::BindRepeating(
+                   &DesktopCloudPolicyStore::InstallLoadedPolicyAfterValidation,
+                   weak_factory_.GetWeakPtr(), doing_key_rotation,
+                   result.key.has_signing_key() ? result.key.signing_key()
+                                                : std::string()));
       break;
     }
     default:
@@ -285,14 +267,14 @@
   }
 }
 
-void UserCloudPolicyStore::InstallLoadedPolicyAfterValidation(
+void DesktopCloudPolicyStore::InstallLoadedPolicyAfterValidation(
     bool doing_key_rotation,
     const std::string& signing_key,
     UserCloudPolicyValidator* validator) {
+  // TODO(zmin): metrics
   UMA_HISTOGRAM_ENUMERATION(
       "Enterprise.UserCloudPolicyStore.LoadValidationStatus",
-      validator->status(),
-      CloudPolicyValidatorBase::VALIDATION_STATUS_SIZE);
+      validator->status(), CloudPolicyValidatorBase::VALIDATION_STATUS_SIZE);
   validation_status_ = validator->status();
   if (!validator->success()) {
     DVLOG(1) << "Validation failed: status=" << validation_status_;
@@ -301,8 +283,8 @@
     return;
   }
 
-  DVLOG(1) << "Validation succeeded - installing policy with dm_token: " <<
-      validator->policy_data()->request_token();
+  DVLOG(1) << "Validation succeeded - installing policy with dm_token: "
+           << validator->policy_data()->request_token();
   DVLOG(1) << "Device ID: " << validator->policy_data()->device_id();
 
   // If we're doing a key rotation, clear the public key version so a future
@@ -321,15 +303,73 @@
   NotifyStoreLoaded();
 }
 
-void UserCloudPolicyStore::Store(const em::PolicyFetchResponse& policy) {
+void DesktopCloudPolicyStore::Store(const em::PolicyFetchResponse& policy) {
   // Stop any pending requests to store policy, then validate the new policy
   // before storing it.
   weak_factory_.InvalidateWeakPtrs();
   std::unique_ptr<em::PolicyFetchResponse> policy_copy(
       new em::PolicyFetchResponse(policy));
-  Validate(std::move(policy_copy), std::unique_ptr<em::PolicySigningKey>(),
-           true, base::Bind(&UserCloudPolicyStore::StorePolicyAfterValidation,
-                            weak_factory_.GetWeakPtr()));
+  Validate(
+      std::move(policy_copy), std::unique_ptr<em::PolicySigningKey>(), true,
+      base::BindRepeating(&DesktopCloudPolicyStore::StorePolicyAfterValidation,
+                          weak_factory_.GetWeakPtr()));
+}
+
+void DesktopCloudPolicyStore::StorePolicyAfterValidation(
+    UserCloudPolicyValidator* validator) {
+  UMA_HISTOGRAM_ENUMERATION(
+      "Enterprise.UserCloudPolicyStore.StoreValidationStatus",
+      validator->status(), CloudPolicyValidatorBase::VALIDATION_STATUS_SIZE);
+  validation_status_ = validator->status();
+  DVLOG(1) << "Policy validation complete: status = " << validation_status_;
+  if (!validator->success()) {
+    status_ = STATUS_VALIDATION_ERROR;
+    NotifyStoreError();
+    return;
+  }
+
+  // Persist the validated policy (just fire a task - don't bother getting a
+  // reply because we can't do anything if it fails).
+  background_task_runner()->PostTask(
+      FROM_HERE,
+      base::BindRepeating(&StorePolicyToDiskOnBackgroundThread, policy_path_,
+                          key_path_, *validator->policy()));
+
+  // If the key was rotated, update our local cache of the key.
+  if (validator->policy()->has_new_public_key())
+    persisted_policy_key_ = validator->policy()->new_public_key();
+
+  InstallPolicy(std::move(validator->policy_data()),
+                std::move(validator->payload()), persisted_policy_key_);
+  status_ = STATUS_OK;
+  NotifyStoreLoaded();
+}
+
+UserCloudPolicyStore::UserCloudPolicyStore(
+    const base::FilePath& policy_path,
+    const base::FilePath& key_path,
+    scoped_refptr<base::SequencedTaskRunner> background_task_runner)
+    : DesktopCloudPolicyStore(policy_path,
+                              key_path,
+                              background_task_runner,
+                              PolicyScope::POLICY_SCOPE_USER) {}
+
+UserCloudPolicyStore::~UserCloudPolicyStore() {}
+
+// static
+std::unique_ptr<UserCloudPolicyStore> UserCloudPolicyStore::Create(
+    const base::FilePath& profile_path,
+    scoped_refptr<base::SequencedTaskRunner> background_task_runner) {
+  base::FilePath policy_path =
+      profile_path.Append(kPolicyDir).Append(kPolicyCacheFile);
+  base::FilePath key_path =
+      profile_path.Append(kPolicyDir).Append(kKeyCacheFile);
+  return base::WrapUnique(
+      new UserCloudPolicyStore(policy_path, key_path, background_task_runner));
+}
+
+void UserCloudPolicyStore::SetSigninUsername(const std::string& username) {
+  signin_username_ = username;
 }
 
 void UserCloudPolicyStore::Validate(
@@ -418,34 +458,5 @@
   }
 }
 
-void UserCloudPolicyStore::StorePolicyAfterValidation(
-    UserCloudPolicyValidator* validator) {
-  UMA_HISTOGRAM_ENUMERATION(
-      "Enterprise.UserCloudPolicyStore.StoreValidationStatus",
-      validator->status(),
-      CloudPolicyValidatorBase::VALIDATION_STATUS_SIZE);
-  validation_status_ = validator->status();
-  DVLOG(1) << "Policy validation complete: status = " << validation_status_;
-  if (!validator->success()) {
-    status_ = STATUS_VALIDATION_ERROR;
-    NotifyStoreError();
-    return;
-  }
-
-  // Persist the validated policy (just fire a task - don't bother getting a
-  // reply because we can't do anything if it fails).
-  background_task_runner()->PostTask(
-      FROM_HERE, base::Bind(&StorePolicyToDiskOnBackgroundThread, policy_path_,
-                            key_path_, *validator->policy()));
-
-  // If the key was rotated, update our local cache of the key.
-  if (validator->policy()->has_new_public_key())
-    persisted_policy_key_ = validator->policy()->new_public_key();
-
-  InstallPolicy(std::move(validator->policy_data()),
-                std::move(validator->payload()), persisted_policy_key_);
-  status_ = STATUS_OK;
-  NotifyStoreLoaded();
-}
 
 }  // namespace policy
diff --git a/components/policy/core/common/cloud/user_cloud_policy_store.h b/components/policy/core/common/cloud/user_cloud_policy_store.h
index 39f28bbf..6d62685e 100644
--- a/components/policy/core/common/cloud/user_cloud_policy_store.h
+++ b/components/policy/core/common/cloud/user_cloud_policy_store.h
@@ -7,7 +7,6 @@
 
 #include <string>
 
-#include "base/compiler_specific.h"
 #include "base/files/file_path.h"
 #include "base/macros.h"
 #include "base/memory/weak_ptr.h"
@@ -21,6 +20,74 @@
 
 namespace policy {
 
+// Implements a cloud policy store that stores policy on desktop. This is used
+// on (non-chromeos) platforms that do not have a secure storage
+// implementation.
+class POLICY_EXPORT DesktopCloudPolicyStore : public UserCloudPolicyStoreBase {
+ public:
+  DesktopCloudPolicyStore(
+      const base::FilePath& policy_file,
+      const base::FilePath& key_file,
+      scoped_refptr<base::SequencedTaskRunner> background_task_runner,
+      PolicyScope policy_scope);
+  ~DesktopCloudPolicyStore() override;
+
+  // Loads policy immediately on the current thread. Virtual for mocks.
+  virtual void LoadImmediately();
+
+  // Deletes any existing policy blob and notifies observers via OnStoreLoaded()
+  // that the blob has changed. Virtual for mocks.
+  virtual void Clear();
+
+  // CloudPolicyStore implementation.
+  void Load() override;
+  void Store(const enterprise_management::PolicyFetchResponse& policy) override;
+
+ protected:
+  // Callback invoked when a new policy has been loaded from disk. If
+  // |validate_in_background| is true, then policy is validated via a background
+  // thread.
+  void PolicyLoaded(bool validate_in_background,
+                    struct PolicyLoadResult policy_load_result);
+
+  // Starts policy blob validation. |callback| is invoked once validation is
+  // complete. If |validate_in_background| is true, then the validation work
+  // occurs on a background thread (results are sent back to the calling
+  // thread).
+  virtual void Validate(
+      std::unique_ptr<enterprise_management::PolicyFetchResponse> policy,
+      std::unique_ptr<enterprise_management::PolicySigningKey> key,
+      bool validate_in_background,
+      const UserCloudPolicyValidator::CompletionCallback& callback) = 0;
+
+  // Callback invoked to install a just-loaded policy after validation has
+  // finished.
+  void InstallLoadedPolicyAfterValidation(bool doing_key_rotation,
+                                          const std::string& signing_key,
+                                          UserCloudPolicyValidator* validator);
+
+  // Callback invoked to store the policy after validation has finished.
+  void StorePolicyAfterValidation(UserCloudPolicyValidator* validator);
+
+  // The current key used to verify signatures of policy. This value is
+  // eventually consistent with the one persisted in the key cache file. This
+  // is, generally, different from |policy_signature_public_key_| member of
+  // the base class CloudPolicyStore, which always corresponds to the currently
+  // effective policy.
+  std::string persisted_policy_key_;
+
+  // Path to file where we store persisted policy.
+  base::FilePath policy_path_;
+
+  // Path to file where we store the signing key for the policy blob.
+  base::FilePath key_path_;
+
+  // WeakPtrFactory used to create callbacks for validating and storing policy.
+  base::WeakPtrFactory<DesktopCloudPolicyStore> weak_factory_;
+
+  DISALLOW_COPY_AND_ASSIGN(DesktopCloudPolicyStore);
+};
+
 // Implements a cloud policy store that is stored in a simple file in the user's
 // profile directory. This is used on (non-chromeos) platforms that do not have
 // a secure storage implementation.
@@ -30,7 +97,7 @@
 // file and is itself verified against the verification public key before using
 // it to verify the policy signature. During the store operation, the key cache
 // file is updated whenever the key rotation happens.
-class POLICY_EXPORT UserCloudPolicyStore : public UserCloudPolicyStoreBase {
+class POLICY_EXPORT UserCloudPolicyStore : public DesktopCloudPolicyStore {
  public:
   // Creates a policy store associated with a signed-in (or in the progress of
   // it) user.
@@ -52,61 +119,16 @@
   // Sets the username from signin for validation of the policy.
   void SetSigninUsername(const std::string& username);
 
-  // Loads policy immediately on the current thread. Virtual for mocks.
-  virtual void LoadImmediately();
-
-  // Deletes any existing policy blob and notifies observers via OnStoreLoaded()
-  // that the blob has changed. Virtual for mocks.
-  virtual void Clear();
-
-  // CloudPolicyStore implementation.
-  void Load() override;
-  void Store(const enterprise_management::PolicyFetchResponse& policy) override;
-
  private:
-  // Callback invoked when a new policy has been loaded from disk. If
-  // |validate_in_background| is true, then policy is validated via a background
-  // thread.
-  void PolicyLoaded(bool validate_in_background,
-                    struct PolicyLoadResult policy_load_result);
-
-  // Starts policy blob validation. |callback| is invoked once validation is
-  // complete. If |validate_in_background| is true, then the validation work
-  // occurs on a background thread (results are sent back to the calling
-  // thread).
   void Validate(
       std::unique_ptr<enterprise_management::PolicyFetchResponse> policy,
       std::unique_ptr<enterprise_management::PolicySigningKey> key,
       bool validate_in_background,
-      const UserCloudPolicyValidator::CompletionCallback& callback);
-
-  // Callback invoked to install a just-loaded policy after validation has
-  // finished.
-  void InstallLoadedPolicyAfterValidation(bool doing_key_rotation,
-                                          const std::string& signing_key,
-                                          UserCloudPolicyValidator* validator);
-
-  // Callback invoked to store the policy after validation has finished.
-  void StorePolicyAfterValidation(UserCloudPolicyValidator* validator);
-
-  // The current key used to verify signatures of policy. This value is
-  // eventually consistent with the one persisted in the key cache file. This
-  // is, generally, different from |policy_signature_public_key_|, which always
-  // corresponds to the currently effective policy.
-  std::string persisted_policy_key_;
-
-  // Path to file where we store persisted policy.
-  base::FilePath policy_path_;
-
-  // Path to file where we store the signing key for the policy blob.
-  base::FilePath key_path_;
+      const UserCloudPolicyValidator::CompletionCallback& callback) override;
 
   // The username from signin for validation of the policy.
   std::string signin_username_;
 
-  // WeakPtrFactory used to create callbacks for validating and storing policy.
-  base::WeakPtrFactory<UserCloudPolicyStore> weak_factory_;
-
   DISALLOW_COPY_AND_ASSIGN(UserCloudPolicyStore);
 };
 
diff --git a/components/policy/core/common/cloud/user_cloud_policy_store_base.cc b/components/policy/core/common/cloud/user_cloud_policy_store_base.cc
index a93a5f33..ae8d338 100644
--- a/components/policy/core/common/cloud/user_cloud_policy_store_base.cc
+++ b/components/policy/core/common/cloud/user_cloud_policy_store_base.cc
@@ -18,11 +18,14 @@
 // generated code in policy/cloud_policy_generated.cc.
 void DecodePolicy(const enterprise_management::CloudPolicySettings& policy,
                   base::WeakPtr<CloudExternalDataManager> external_data_manager,
-                  PolicyMap* policies);
+                  PolicyMap* policies,
+                  PolicyScope scope);
 
 UserCloudPolicyStoreBase::UserCloudPolicyStoreBase(
-    scoped_refptr<base::SequencedTaskRunner> background_task_runner)
-    : background_task_runner_(background_task_runner) {}
+    scoped_refptr<base::SequencedTaskRunner> background_task_runner,
+    PolicyScope policy_scope)
+    : background_task_runner_(background_task_runner),
+      policy_scope_(policy_scope) {}
 
 UserCloudPolicyStoreBase::~UserCloudPolicyStoreBase() {
 }
@@ -51,7 +54,7 @@
     const std::string& policy_signature_public_key) {
   // Decode the payload.
   policy_map_.Clear();
-  DecodePolicy(*payload, external_data_manager(), &policy_map_);
+  DecodePolicy(*payload, external_data_manager(), &policy_map_, policy_scope_);
   policy_ = std::move(policy_data);
   policy_signature_public_key_ = policy_signature_public_key;
 }
diff --git a/components/policy/core/common/cloud/user_cloud_policy_store_base.h b/components/policy/core/common/cloud/user_cloud_policy_store_base.h
index 72cfb00..ef99700 100644
--- a/components/policy/core/common/cloud/user_cloud_policy_store_base.h
+++ b/components/policy/core/common/cloud/user_cloud_policy_store_base.h
@@ -13,6 +13,7 @@
 #include "base/memory/weak_ptr.h"
 #include "components/policy/core/common/cloud/cloud_policy_store.h"
 #include "components/policy/core/common/cloud/cloud_policy_validator.h"
+#include "components/policy/core/common/policy_types.h"
 #include "components/policy/policy_export.h"
 
 namespace base {
@@ -25,8 +26,9 @@
 // functionality.
 class POLICY_EXPORT UserCloudPolicyStoreBase : public CloudPolicyStore {
  public:
-  explicit UserCloudPolicyStoreBase(
-      scoped_refptr<base::SequencedTaskRunner> background_task_runner);
+  UserCloudPolicyStoreBase(
+      scoped_refptr<base::SequencedTaskRunner> background_task_runner,
+      PolicyScope policy_scope);
   ~UserCloudPolicyStoreBase() override;
 
  protected:
@@ -50,6 +52,7 @@
  private:
   // Task runner for background file operations.
   scoped_refptr<base::SequencedTaskRunner> background_task_runner_;
+  PolicyScope policy_scope_;
 
   DISALLOW_COPY_AND_ASSIGN(UserCloudPolicyStoreBase);
 };
diff --git a/components/policy/tools/generate_policy_source.py b/components/policy/tools/generate_policy_source.py
index 4bc376e..b83486eb 100755
--- a/components/policy/tools/generate_policy_source.py
+++ b/components/policy/tools/generate_policy_source.py
@@ -1168,7 +1168,8 @@
 
 void DecodePolicy(const em::CloudPolicySettings& policy,
                   base::WeakPtr<CloudExternalDataManager> external_data_manager,
-                  PolicyMap* map) {
+                  PolicyMap* map,
+                  PolicyScope scope) {
 '''
 
 
@@ -1234,7 +1235,7 @@
           _CreateExternalDataFetcher(policy.policy_type, policy.name))
   f.write('          map->Set(key::k%s, \n' % policy.name)
   f.write('                   level, \n'
-          '                   POLICY_SCOPE_USER, \n'
+          '                   scope, \n'
           '                   POLICY_SOURCE_CLOUD, \n'
           '                   std::move(value), \n'
           '                   std::move(external_data_fetcher));\n'
diff --git a/components/proxy_config/pref_proxy_config_tracker_impl_unittest.cc b/components/proxy_config/pref_proxy_config_tracker_impl_unittest.cc
index a636f25a..5e46c82 100644
--- a/components/proxy_config/pref_proxy_config_tracker_impl_unittest.cc
+++ b/components/proxy_config/pref_proxy_config_tracker_impl_unittest.cc
@@ -141,10 +141,9 @@
   EXPECT_TRUE(actual_config.auto_detect());
 }
 
-// Compares proxy configurations, but allows different identifiers.
+// Compares proxy configurations, but allows different sources.
 MATCHER_P(ProxyConfigMatches, config, "") {
   net::ProxyConfig reference(config);
-  reference.set_id(arg.id());
   return reference.Equals(arg);
 }
 
diff --git a/components/sessions/core/base_session_service.cc b/components/sessions/core/base_session_service.cc
index 4d113ac..3e76628 100644
--- a/components/sessions/core/base_session_service.cc
+++ b/components/sessions/core/base_session_service.cc
@@ -133,7 +133,7 @@
   // opportunity to append more commands.
   delegate_->OnWillSaveCommands();
 
-  if (pending_commands_.empty() && !pending_reset_)
+  if (pending_commands_.empty())
     return;
 
   // We create a new vector which will receive all elements from the
diff --git a/components/sessions/core/in_memory_tab_restore_service.cc b/components/sessions/core/in_memory_tab_restore_service.cc
index bfbf7ae..cdc31f19 100644
--- a/components/sessions/core/in_memory_tab_restore_service.cc
+++ b/components/sessions/core/in_memory_tab_restore_service.cc
@@ -46,11 +46,6 @@
   helper_.ClearEntries();
 }
 
-void InMemoryTabRestoreService::DeleteNavigationEntries(
-    const DeletionPredicate& predicate) {
-  helper_.DeleteNavigationEntries(predicate);
-}
-
 const TabRestoreService::Entries& InMemoryTabRestoreService::entries() const {
   return helper_.entries();
 }
diff --git a/components/sessions/core/in_memory_tab_restore_service.h b/components/sessions/core/in_memory_tab_restore_service.h
index b2ca707..c6174c3 100644
--- a/components/sessions/core/in_memory_tab_restore_service.h
+++ b/components/sessions/core/in_memory_tab_restore_service.h
@@ -39,7 +39,6 @@
   void BrowserClosing(LiveTabContext* context) override;
   void BrowserClosed(LiveTabContext* context) override;
   void ClearEntries() override;
-  void DeleteNavigationEntries(const DeletionPredicate& predicate) override;
   const Entries& entries() const override;
   std::vector<LiveTab*> RestoreMostRecentEntry(
       LiveTabContext* context) override;
diff --git a/components/sessions/core/persistent_tab_restore_service.cc b/components/sessions/core/persistent_tab_restore_service.cc
index 13a51be..d074399 100644
--- a/components/sessions/core/persistent_tab_restore_service.cc
+++ b/components/sessions/core/persistent_tab_restore_service.cc
@@ -352,7 +352,6 @@
 
   // TabRestoreServiceHelper::Observer:
   void OnClearEntries() override;
-  void OnNavigationEntriesDeleted() override;
   void OnRestoreEntryById(SessionID::id_type id,
                           Entries::const_iterator entry_iterator) override;
   void OnAddEntry() override;
@@ -534,16 +533,10 @@
 
   // Schedule a pending reset so that we nuke the file on next write.
   base_session_service_->set_pending_reset(true);
-  base_session_service_->StartSaveTimer();
-}
 
-void PersistentTabRestoreService::Delegate::OnNavigationEntriesDeleted() {
-  // Rewrite all entries.
-  entries_to_write_ = tab_restore_service_helper_->entries().size();
-
-  // Schedule a pending reset so that we nuke the file on next write.
-  base_session_service_->set_pending_reset(true);
-  base_session_service_->StartSaveTimer();
+  // Schedule a command, otherwise if there are no pending commands Save does
+  // nothing.
+  base_session_service_->ScheduleCommand(CreateRestoredEntryCommand(1));
 }
 
 void PersistentTabRestoreService::Delegate::OnRestoreEntryById(
@@ -1127,12 +1120,6 @@
   helper_.ClearEntries();
 }
 
-void PersistentTabRestoreService::DeleteNavigationEntries(
-    const DeletionPredicate& predicate) {
-  DCHECK(IsLoaded());
-  helper_.DeleteNavigationEntries(predicate);
-}
-
 const TabRestoreService::Entries& PersistentTabRestoreService::entries() const {
   return helper_.entries();
 }
diff --git a/components/sessions/core/persistent_tab_restore_service.h b/components/sessions/core/persistent_tab_restore_service.h
index 206ca4ae..9d2eaa86 100644
--- a/components/sessions/core/persistent_tab_restore_service.h
+++ b/components/sessions/core/persistent_tab_restore_service.h
@@ -35,7 +35,6 @@
   void BrowserClosing(LiveTabContext* context) override;
   void BrowserClosed(LiveTabContext* context) override;
   void ClearEntries() override;
-  void DeleteNavigationEntries(const DeletionPredicate& predicate) override;
   const Entries& entries() const override;
   std::vector<LiveTab*> RestoreMostRecentEntry(
       LiveTabContext* context) override;
diff --git a/components/sessions/core/session_backend.cc b/components/sessions/core/session_backend.cc
index 0f276e1..07a807f9 100644
--- a/components/sessions/core/session_backend.cc
+++ b/components/sessions/core/session_backend.cc
@@ -231,8 +231,7 @@
       !AppendCommandsToFile(current_session_file_.get(), commands)) {
     current_session_file_.reset(nullptr);
   }
-  if (!commands.empty())
-    empty_file_ = false;
+  empty_file_ = false;
 }
 
 void SessionBackend::ReadLastSessionCommands(
diff --git a/components/sessions/core/tab_restore_service.h b/components/sessions/core/tab_restore_service.h
index 5a84024..47bd404 100644
--- a/components/sessions/core/tab_restore_service.h
+++ b/components/sessions/core/tab_restore_service.h
@@ -143,8 +143,6 @@
   };
 
   typedef std::list<std::unique_ptr<Entry>> Entries;
-  typedef base::RepeatingCallback<bool(const SerializedNavigationEntry& entry)>
-      DeletionPredicate;
 
   ~TabRestoreService() override;
 
@@ -171,10 +169,6 @@
   // of tabs has changed.
   virtual void ClearEntries() = 0;
 
-  // Removes all SerializedNavigationEntries matching |predicate| and notifies
-  // observers the list of tabs has changed.
-  virtual void DeleteNavigationEntries(const DeletionPredicate& predicate) = 0;
-
   // Returns the entries, ordered with most recently closed entries at the
   // front.
   virtual const Entries& entries() const = 0;
diff --git a/components/sessions/core/tab_restore_service_helper.cc b/components/sessions/core/tab_restore_service_helper.cc
index 89c61dabad..a18f3bc 100644
--- a/components/sessions/core/tab_restore_service_helper.cc
+++ b/components/sessions/core/tab_restore_service_helper.cc
@@ -34,8 +34,6 @@
 
 void TabRestoreServiceHelper::Observer::OnClearEntries() {}
 
-void TabRestoreServiceHelper::Observer::OnNavigationEntriesDeleted() {}
-
 void TabRestoreServiceHelper::Observer::OnRestoreEntryById(
     SessionID::id_type id,
     Entries::const_iterator entry_iterator) {
@@ -142,78 +140,6 @@
   NotifyTabsChanged();
 }
 
-bool TabRestoreServiceHelper::DeleteFromTab(const DeletionPredicate& predicate,
-                                            Tab* tab) {
-  std::vector<SerializedNavigationEntry> new_navigations;
-  int deleted_navigations = 0;
-  for (auto& navigation : tab->navigations) {
-    if (predicate.Run(navigation)) {
-      // If the current navigation is deleted, remove this tab.
-      if (tab->current_navigation_index == navigation.index())
-        return true;
-      deleted_navigations++;
-    } else {
-      // Adjust indices according to number of deleted navigations.
-      if (tab->current_navigation_index == navigation.index())
-        tab->current_navigation_index -= deleted_navigations;
-      navigation.set_index(navigation.index() - deleted_navigations);
-      new_navigations.push_back(std::move(navigation));
-    }
-  }
-  tab->navigations = std::move(new_navigations);
-  DCHECK(tab->navigations.empty() || ValidateTab(*tab));
-  return tab->navigations.empty();
-}
-
-bool TabRestoreServiceHelper::DeleteFromWindow(
-    const DeletionPredicate& predicate,
-    Window* window) {
-  std::vector<std::unique_ptr<Tab>> new_tabs;
-  int deleted_tabs = 0;
-  for (auto& tab : window->tabs) {
-    if (DeleteFromTab(predicate, tab.get())) {
-      if (window->selected_tab_index == tab->tabstrip_index)
-        window->selected_tab_index = 0;
-      deleted_tabs++;
-    } else {
-      // Adjust indices according to number of deleted tabs.
-      if (window->tabs[window->selected_tab_index] == tab)
-        window->selected_tab_index -= deleted_tabs;
-      if (tab->tabstrip_index >= 0)
-        tab->tabstrip_index -= deleted_tabs;
-      new_tabs.push_back(std::move(tab));
-    }
-  }
-  window->tabs = std::move(new_tabs);
-  DCHECK(window->tabs.empty() || ValidateWindow(*window));
-  return window->tabs.empty();
-}
-
-void TabRestoreServiceHelper::DeleteNavigationEntries(
-    const DeletionPredicate& predicate) {
-  Entries new_entries;
-  for (std::unique_ptr<Entry>& entry : entries_) {
-    switch (entry->type) {
-      case TabRestoreService::TAB: {
-        Tab* tab = static_cast<Tab*>(entry.get());
-        if (!DeleteFromTab(predicate, tab))
-          new_entries.push_back(std::move(entry));
-        break;
-      }
-      case TabRestoreService::WINDOW: {
-        Window* window = static_cast<Window*>(entry.get());
-        if (!DeleteFromWindow(predicate, window))
-          new_entries.push_back(std::move(entry));
-        break;
-      }
-    }
-  }
-  entries_ = std::move(new_entries);
-  if (observer_)
-    observer_->OnNavigationEntriesDeleted();
-  NotifyTabsChanged();
-}
-
 const TabRestoreService::Entries& TabRestoreServiceHelper::entries() const {
   return entries_;
 }
diff --git a/components/sessions/core/tab_restore_service_helper.h b/components/sessions/core/tab_restore_service_helper.h
index abeb588d..1d503b08 100644
--- a/components/sessions/core/tab_restore_service_helper.h
+++ b/components/sessions/core/tab_restore_service_helper.h
@@ -31,7 +31,6 @@
 class SESSIONS_EXPORT TabRestoreServiceHelper
     : public base::trace_event::MemoryDumpProvider {
  public:
-  typedef TabRestoreService::DeletionPredicate DeletionPredicate;
   typedef TabRestoreService::Entries Entries;
   typedef TabRestoreService::Entry Entry;
   typedef TabRestoreService::Tab Tab;
@@ -45,9 +44,6 @@
     // Invoked before the entries are cleared.
     virtual void OnClearEntries();
 
-    // Invoked when navigations from entries have been deleted.
-    virtual void OnNavigationEntriesDeleted();
-
     // Invoked before the entry is restored. |entry_iterator| points to the
     // entry corresponding to the session identified by |id|.
     virtual void OnRestoreEntryById(SessionID::id_type id,
@@ -82,8 +78,6 @@
   void BrowserClosing(LiveTabContext* context);
   void BrowserClosed(LiveTabContext* context);
   void ClearEntries();
-  void DeleteNavigationEntries(const DeletionPredicate& predicate);
-
   const Entries& entries() const;
   std::vector<LiveTab*> RestoreMostRecentEntry(LiveTabContext* context);
   std::unique_ptr<Tab> RemoveTabEntryById(SessionID::id_type id);
@@ -150,15 +144,6 @@
   // Validates all the tabs in a window, plus the window's active tab index.
   static bool ValidateWindow(const Window& window);
 
-  // Removes all navigation entries matching |predicate| from |tab|.
-  // Returns true if |tab| should be deleted because it is empty.
-  static bool DeleteFromTab(const DeletionPredicate& predicate, Tab* tab);
-
-  // Removes all navigation entries matching |predicate| from tabs in |window|.
-  // Returns true if |window| should be deleted because it is empty.
-  static bool DeleteFromWindow(const DeletionPredicate& predicate,
-                               Window* window);
-
   // Returns true if |tab| is one we care about restoring.
   bool IsTabInteresting(const Tab& tab);
 
diff --git a/components/sessions/core/tab_restore_service_observer.h b/components/sessions/core/tab_restore_service_observer.h
index 96bb9ba3..7ddd644a 100644
--- a/components/sessions/core/tab_restore_service_observer.h
+++ b/components/sessions/core/tab_restore_service_observer.h
@@ -16,7 +16,7 @@
 class SESSIONS_EXPORT TabRestoreServiceObserver {
  public:
   // Sent when the set of entries changes in some way.
-  virtual void TabRestoreServiceChanged(TabRestoreService* service) {}
+  virtual void TabRestoreServiceChanged(TabRestoreService* service) = 0;
 
   // Sent to all remaining Observers when TabRestoreService's
   // destructor is run.
diff --git a/components/startup_metric_utils/browser/startup_metric_utils.cc b/components/startup_metric_utils/browser/startup_metric_utils.cc
index b83caaf..a6ef802 100644
--- a/components/startup_metric_utils/browser/startup_metric_utils.cc
+++ b/components/startup_metric_utils/browser/startup_metric_utils.cc
@@ -19,8 +19,6 @@
 #include "base/profiler/stack_sampling_profiler.h"
 #include "base/strings/string_number_conversions.h"
 #include "base/sys_info.h"
-#include "base/task_scheduler/post_task.h"
-#include "base/task_scheduler/task_traits.h"
 #include "base/threading/platform_thread.h"
 #include "base/trace_event/trace_event.h"
 #include "build/build_config.h"
@@ -322,10 +320,10 @@
 }
 
 // On Windows, records the number of hard-faults that have occurred in the
-// current chrome.exe process since it was started. This function uses an
-// expensive system call and should preferably be used in a background thread.
-#if defined(OS_WIN)
+// current chrome.exe process since it was started. This is a nop on other
+// platforms.
 void RecordHardFaultHistogram() {
+#if defined(OS_WIN)
   uint32_t hard_fault_count = 0;
 
   // Don't record histograms if unable to get the hard fault count.
@@ -376,8 +374,8 @@
         base::HistogramBase::kUmaTargetedHistogramFlag)
         ->Add(g_startup_temperature);
   }
-}
 #endif  // defined(OS_WIN)
+}
 
 // Converts a base::Time value to a base::TimeTicks value. The conversion isn't
 // exact, but by capturing Time::Now() as early as possible, the likelihood of a
@@ -570,14 +568,7 @@
   // histograms depend on it setting |g_startup_temperature| and
   // |g_startups_with_current_version|.
   RecordSameVersionStartupCount(pref_service);
-#if defined(OS_WIN)
-  // Record the hard page fault count in a background thread as this is quite
-  // expensive to compute.
-  base::PostTaskWithTraits(FROM_HERE,
-                           {base::TaskPriority::BACKGROUND,
-                            base::TaskShutdownBehavior::CONTINUE_ON_SHUTDOWN},
-                           base::BindOnce(&RecordHardFaultHistogram));
-#endif
+  RecordHardFaultHistogram();
 
   // Record timing of the browser message-loop start time.
   base::StackSamplingProfiler::SetProcessMilestone(
diff --git a/components/viz/common/quads/compositor_frame_metadata.h b/components/viz/common/quads/compositor_frame_metadata.h
index 9d510fee..c6033d9d 100644
--- a/components/viz/common/quads/compositor_frame_metadata.h
+++ b/components/viz/common/quads/compositor_frame_metadata.h
@@ -47,7 +47,6 @@
   gfx::SizeF root_layer_size;
   float min_page_scale_factor = 0.f;
   float max_page_scale_factor = 0.f;
-  bool root_overflow_x_hidden = false;
   bool root_overflow_y_hidden = false;
   bool may_contain_video = false;
 
diff --git a/components/viz/service/main/viz_main_impl.cc b/components/viz/service/main/viz_main_impl.cc
index d06345c..4bdd159 100644
--- a/components/viz/service/main/viz_main_impl.cc
+++ b/components/viz/service/main/viz_main_impl.cc
@@ -92,7 +92,7 @@
       associated_binding_(this) {
   // TODO(crbug.com/609317): Remove this when Mus Window Server and GPU are
   // split into separate processes. Until then this is necessary to be able to
-  // run Mushrome (chrome --mus) with Mus running in the browser process.
+  // run Mushrome (chrome with mus) with Mus running in the browser process.
   if (!base::PowerMonitor::Get()) {
     power_monitor_ = std::make_unique<base::PowerMonitor>(
         std::make_unique<base::PowerMonitorDeviceSource>());
diff --git a/content/BUILD.gn b/content/BUILD.gn
index a8ef40a..2092909 100644
--- a/content/BUILD.gn
+++ b/content/BUILD.gn
@@ -9,7 +9,10 @@
 # Applied by targets internal to content.
 config("content_implementation") {
   defines = [ "CONTENT_IMPLEMENTATION" ]
-  configs = [ "//build/config/compiler:wexit_time_destructors" ]
+  configs = [
+    "//build/config/compiler:wexit_time_destructors",
+    "//build/config/compiler:noshadowing",
+  ]
 }
 
 # When targets depend on, e.g. //content/public/browser, what happens? To
diff --git a/content/app/content_main_runner.cc b/content/app/content_main_runner.cc
index 9a4d63e..00864253 100644
--- a/content/app/content_main_runner.cc
+++ b/content/app/content_main_runner.cc
@@ -464,12 +464,6 @@
     ui_task_ = params.ui_task;
     created_main_parts_closure_ = params.created_main_parts_closure;
 
-    create_discardable_memory_ = params.create_discardable_memory;
-
-#if defined(USE_AURA)
-    env_mode_ = params.env_mode;
-#endif
-
 #if defined(OS_WIN)
     sandbox_info_ = *params.sandbox_info;
 #else  // !OS_WIN
@@ -705,10 +699,6 @@
 #elif defined(OS_MACOSX)
     main_params.autorelease_pool = autorelease_pool_;
 #endif
-#if defined(USE_AURA)
-    main_params.env_mode = env_mode_;
-#endif
-    main_params.create_discardable_memory = create_discardable_memory_;
 
     return RunNamedProcessTypeMain(process_type, main_params, delegate_);
   }
@@ -765,12 +755,6 @@
 
   CreatedMainPartsClosure* created_main_parts_closure_ = nullptr;
 
-#if defined(USE_AURA)
-  aura::Env::Mode env_mode_ = aura::Env::Mode::LOCAL;
-#endif
-
-  bool create_discardable_memory_ = true;
-
   DISALLOW_COPY_AND_ASSIGN(ContentMainRunnerImpl);
 };
 
diff --git a/content/browser/BUILD.gn b/content/browser/BUILD.gn
index 81102b9..4ae093e 100644
--- a/content/browser/BUILD.gn
+++ b/content/browser/BUILD.gn
@@ -1152,8 +1152,6 @@
     "message_port_provider.cc",
     "mime_registry_impl.cc",
     "mime_registry_impl.h",
-    "mus_util.cc",
-    "mus_util.h",
     "net/browser_online_state_observer.cc",
     "net/browser_online_state_observer.h",
     "net/network_errors_listing_ui.cc",
diff --git a/content/browser/accessibility/browser_accessibility_cocoa.mm b/content/browser/accessibility/browser_accessibility_cocoa.mm
index bdb1aa2..7c847c86 100644
--- a/content/browser/accessibility/browser_accessibility_cocoa.mm
+++ b/content/browser/accessibility/browser_accessibility_cocoa.mm
@@ -1585,14 +1585,14 @@
   if (([role isEqualToString:NSAccessibilityGroupRole] ||
        [role isEqualToString:NSAccessibilityRadioButtonRole]) &&
       !browserAccessibility_->IsWebAreaForPresentationalIframe()) {
-    std::string role;
-    if (browserAccessibility_->GetHtmlAttribute("role", &role)) {
+    std::string role_attribute;
+    if (browserAccessibility_->GetHtmlAttribute("role", &role_attribute)) {
       ax::mojom::Role internalRole = [self internalRole];
       if ((internalRole != ax::mojom::Role::kGroup &&
            internalRole != ax::mojom::Role::kListItem) ||
           internalRole == ax::mojom::Role::kTab) {
         // TODO(dtseng): This is not localized; see crbug/84814.
-        return base::SysUTF8ToNSString(role);
+        return base::SysUTF8ToNSString(role_attribute);
       }
     }
   }
diff --git a/content/browser/accessibility/web_contents_accessibility_android.cc b/content/browser/accessibility/web_contents_accessibility_android.cc
index 4421490..84d2116 100644
--- a/content/browser/accessibility/web_contents_accessibility_android.cc
+++ b/content/browser/accessibility/web_contents_accessibility_android.cc
@@ -538,15 +538,16 @@
   if (obj.is_null())
     return false;
 
-  if (!Java_WebContentsAccessibilityImpl_onHoverEvent(env, obj,
-                                                      event.GetAction()))
+  if (!Java_WebContentsAccessibilityImpl_onHoverEvent(
+          env, obj,
+          ui::MotionEventAndroid::GetAndroidAction(event.GetAction())))
     return false;
 
   // |HitTest| sends an IPC to the render process to do the hit testing.
   // The response is handled by HandleHover when it returns.
   // Hover event was consumed by accessibility by now. Return true to
   // stop the event from proceeding.
-  if (event.GetAction() != ui::MotionEvent::ACTION_HOVER_EXIT &&
+  if (event.GetAction() != ui::MotionEvent::Action::HOVER_EXIT &&
       root_manager_) {
     gfx::PointF point =
         IsUseZoomForDSFEnabled() ? event.GetPointPix() : event.GetPoint();
diff --git a/content/browser/browser_main_loop.cc b/content/browser/browser_main_loop.cc
index 5c8ad0c6..e903e78 100644
--- a/content/browser/browser_main_loop.cc
+++ b/content/browser/browser_main_loop.cc
@@ -80,7 +80,6 @@
 #include "content/browser/media/media_internals.h"
 #include "content/browser/memory/memory_coordinator_impl.h"
 #include "content/browser/memory/swap_metrics_delegate_uma.h"
-#include "content/browser/mus_util.h"
 #include "content/browser/net/browser_online_state_observer.h"
 #include "content/browser/renderer_host/media/media_stream_manager.h"
 #include "content/browser/renderer_host/render_process_host_impl.h"
@@ -133,8 +132,7 @@
 #include "skia/ext/skia_memory_dump_provider.h"
 #include "sql/sql_memory_dump_provider.h"
 #include "ui/base/clipboard/clipboard.h"
-#include "ui/base/ui_base_switches.h"
-#include "ui/base/ui_base_switches_util.h"
+#include "ui/base/ui_base_features.h"
 #include "ui/display/display_switches.h"
 #include "ui/gfx/switches.h"
 
@@ -798,7 +796,7 @@
         BrowserThread::GetTaskRunnerForThread(BrowserThread::UI));
   }
 
-  if (parameters_.create_discardable_memory) {
+  if (!base::FeatureList::IsEnabled(::features::kMash)) {
     discardable_shared_memory_manager_ =
         std::make_unique<discardable_memory::DiscardableSharedMemoryManager>();
     // TODO(boliu): kSingleProcess check is a temporary workaround for
@@ -1312,7 +1310,7 @@
   // CompositingModeReporter.
   return;
 #else
-  if (IsUsingMus()) {
+  if (features::IsMusEnabled()) {
     // Mus == ChromeOS, which doesn't support software compositing, so no need
     // to report compositing mode.
     return;
@@ -1350,7 +1348,7 @@
   InitializeMojo();
 
 #if BUILDFLAG(ENABLE_MUS)
-  if (IsUsingMus()) {
+  if (features::IsMusEnabled()) {
     base::CommandLine::ForCurrentProcess()->AppendSwitch(
         switches::kEnableSurfaceSynchronization);
   }
@@ -1374,7 +1372,7 @@
       BrowserThread::GetTaskRunnerForThread(BrowserThread::IO));
 
   // If mus is not hosting viz, then the browser must.
-  bool browser_is_viz_host = !switches::IsMusHostingViz();
+  bool browser_is_viz_host = !base::FeatureList::IsEnabled(::features::kMash);
 
   bool always_uses_gpu = true;
   bool established_gpu_channel = false;
@@ -1616,11 +1614,12 @@
 
   // Env creates the compositor. Aura widgets need the compositor to be created
   // before they can be initialized by the browser.
-  env_ = aura::Env::CreateInstance(parameters_.env_mode);
+  env_ = aura::Env::CreateInstance(
+      features::IsMusEnabled() ? aura::Env::Mode::MUS : aura::Env::Mode::LOCAL);
 #endif  // defined(USE_AURA)
 
 #if BUILDFLAG(ENABLE_MUS)
-  if (parsed_command_line_.HasSwitch(switches::kMus))
+  if (features::IsMusEnabled())
     image_cursors_set_ = std::make_unique<ui::ImageCursorsSet>();
 #endif
 
diff --git a/content/browser/browser_plugin/browser_plugin_guest.cc b/content/browser/browser_plugin/browser_plugin_guest.cc
index 1abea102..5f9ea0bf0 100644
--- a/content/browser/browser_plugin/browser_plugin_guest.cc
+++ b/content/browser/browser_plugin/browser_plugin_guest.cc
@@ -26,7 +26,6 @@
 #include "content/browser/frame_host/render_frame_proxy_host.h"
 #include "content/browser/frame_host/render_widget_host_view_guest.h"
 #include "content/browser/loader/resource_dispatcher_host_impl.h"
-#include "content/browser/mus_util.h"
 #include "content/browser/renderer_host/cursor_manager.h"
 #include "content/browser/renderer_host/render_view_host_impl.h"
 #include "content/browser/renderer_host/render_widget_host_impl.h"
@@ -52,7 +51,7 @@
 #include "content/public/browser/render_widget_host_view.h"
 #include "content/public/browser/web_contents_observer.h"
 #include "content/public/common/drop_data.h"
-#include "ui/base/ui_base_switches_util.h"
+#include "ui/base/ui_base_features.h"
 #include "ui/events/blink/web_input_event_traits.h"
 #include "ui/gfx/geometry/size_conversions.h"
 
@@ -419,7 +418,7 @@
 void BrowserPluginGuest::SetChildFrameSurface(
     const viz::SurfaceInfo& surface_info) {
   has_attached_since_surface_set_ = false;
-  if (!switches::IsMusHostingViz()) {
+  if (!base::FeatureList::IsEnabled(::features::kMash)) {
     SendMessageToEmbedder(
         std::make_unique<BrowserPluginMsg_SetChildFrameSurface>(
             browser_plugin_instance_id(), surface_info));
@@ -672,7 +671,7 @@
   // In case we've created a new guest render process after a crash, let the
   // associated BrowserPlugin know. We only need to send this if we're attached,
   // as guest_crashed_ is cleared automatically on attach anyways.
-  if (attached() && !switches::IsMusHostingViz()) {
+  if (attached() && !base::FeatureList::IsEnabled(::features::kMash)) {
     RenderWidgetHostViewGuest* rwhv = static_cast<RenderWidgetHostViewGuest*>(
         web_contents()->GetRenderWidgetHostView());
     if (rwhv) {
diff --git a/content/browser/compositor/gpu_process_transport_factory.cc b/content/browser/compositor/gpu_process_transport_factory.cc
index e798e61c..c054c17 100644
--- a/content/browser/compositor/gpu_process_transport_factory.cc
+++ b/content/browser/compositor/gpu_process_transport_factory.cc
@@ -48,7 +48,6 @@
 #include "content/browser/compositor/software_browser_compositor_output_surface.h"
 #include "content/browser/gpu/compositor_util.h"
 #include "content/browser/gpu/gpu_data_manager_impl.h"
-#include "content/browser/mus_util.h"
 #include "content/browser/renderer_host/render_widget_host_impl.h"
 #include "content/common/gpu_stream_constants.h"
 #include "content/public/common/content_switches.h"
@@ -65,6 +64,7 @@
 #include "services/service_manager/runner/common/client_util.h"
 #include "services/ui/public/cpp/gpu/context_provider_command_buffer.h"
 #include "third_party/khronos/GLES2/gl2.h"
+#include "ui/base/ui_base_features.h"
 #include "ui/base/ui_base_switches_util.h"
 #include "ui/compositor/compositor.h"
 #include "ui/compositor/compositor_switches.h"
@@ -232,7 +232,7 @@
     return base::WrapUnique(new viz::SoftwareOutputDevice);
 
 #if defined(USE_AURA)
-  if (switches::IsMusHostingViz()) {
+  if (base::FeatureList::IsEnabled(features::kMash)) {
     NOTREACHED();
     return nullptr;
   }
diff --git a/content/browser/devtools/protocol/devtools_protocol_browsertest.cc b/content/browser/devtools/protocol/devtools_protocol_browsertest.cc
index 7d7f4995..477b741 100644
--- a/content/browser/devtools/protocol/devtools_protocol_browsertest.cc
+++ b/content/browser/devtools/protocol/devtools_protocol_browsertest.cc
@@ -139,7 +139,7 @@
 
   // JavaScriptDialogManager
   void RunJavaScriptDialog(WebContents* web_contents,
-                           const GURL& alerting_frame_url,
+                           RenderFrameHost* render_frame_host,
                            JavaScriptDialogType dialog_type,
                            const base::string16& message_text,
                            const base::string16& default_prompt_text,
diff --git a/content/browser/devtools/protocol/page_handler.cc b/content/browser/devtools/protocol/page_handler.cc
index 11d70b5..408a3c5 100644
--- a/content/browser/devtools/protocol/page_handler.cc
+++ b/content/browser/devtools/protocol/page_handler.cc
@@ -799,7 +799,7 @@
 }
 
 void PageHandler::InnerSwapCompositorFrame() {
-  if (!host_ || !host_->GetView())
+  if (!host_)
     return;
 
   if (frames_in_flight_ > kMaxScreencastFramesInFlight)
@@ -808,56 +808,66 @@
   if (++frame_counter_ % capture_every_nth_frame_)
     return;
 
-  RenderWidgetHostViewBase* view = static_cast<RenderWidgetHostViewBase*>(
-      host_->GetView());
-  // TODO(vkuzkokov): do not use previous frame metadata.
-  // TODO(miu): RWHV to provide an API to provide actual rendering size.
-  // http://crbug.com/73362
-  viz::CompositorFrameMetadata& metadata = last_compositor_frame_metadata_;
+  RenderWidgetHostViewBase* const view =
+      static_cast<RenderWidgetHostViewBase*>(host_->GetView());
+  if (!view || !view->IsSurfaceAvailableForCopy())
+    return;
 
-  float css_to_dip = metadata.page_scale_factor;
-  if (IsUseZoomForDSFEnabled())
-    css_to_dip /= metadata.device_scale_factor;
-  gfx::SizeF viewport_size_dip =
-      gfx::ScaleSize(metadata.scrollable_viewport_size, css_to_dip);
-  gfx::SizeF screen_size_dip =
-      gfx::ScaleSize(gfx::SizeF(view->GetPhysicalBackingSize()),
-                     1 / metadata.device_scale_factor);
-
-  content::ScreenInfo screen_info;
-  view->GetScreenInfo(&screen_info);
-  double device_scale_factor = screen_info.device_scale_factor;
+  // Determine the snapshot size that best-fits the Surface's content to the
+  // remote's requested image size.
+  const gfx::Size& surface_size = view->GetPhysicalBackingSize();
+  if (surface_size.IsEmpty())
+    return;  // Nothing to copy (and avoid divide-by-zero below).
   double scale = 1;
-
   if (screencast_max_width_ > 0) {
-    double max_width_dip = screencast_max_width_ / device_scale_factor;
-    scale = std::min(scale, max_width_dip / screen_size_dip.width());
+    scale = std::min(scale, static_cast<double>(screencast_max_width_) /
+                                surface_size.width());
   }
   if (screencast_max_height_ > 0) {
-    double max_height_dip = screencast_max_height_ / device_scale_factor;
-    scale = std::min(scale, max_height_dip / screen_size_dip.height());
+    scale = std::min(scale, static_cast<double>(screencast_max_height_) /
+                                surface_size.height());
   }
+  const gfx::Size snapshot_size =
+      gfx::ToRoundedSize(gfx::ScaleSize(gfx::SizeF(surface_size), scale));
+  if (snapshot_size.IsEmpty())
+    return;
 
-  if (scale <= 0)
-    scale = 0.1;
+  // Build the ScreencastFrameMetadata associated with this capture attempt.
+  const auto& metadata = last_compositor_frame_metadata_;
+  if (metadata.device_scale_factor == 0)
+    return;
+  const gfx::SizeF content_size_dip = gfx::ScaleSize(
+      gfx::SizeF(surface_size), 1 / metadata.device_scale_factor);
+  float top_offset_dip =
+      metadata.top_controls_height * metadata.top_controls_shown_ratio;
+  if (IsUseZoomForDSFEnabled())
+    top_offset_dip /= metadata.device_scale_factor;
+  std::unique_ptr<Page::ScreencastFrameMetadata> page_metadata =
+      Page::ScreencastFrameMetadata::Create()
+          .SetPageScaleFactor(metadata.page_scale_factor)
+          .SetOffsetTop(top_offset_dip)
+          .SetDeviceWidth(content_size_dip.width())
+          .SetDeviceHeight(content_size_dip.height())
+          .SetScrollOffsetX(metadata.root_scroll_offset.x())
+          .SetScrollOffsetY(metadata.root_scroll_offset.y())
+          .SetTimestamp(base::Time::Now().ToDoubleT())
+          .Build();
 
-  gfx::Size snapshot_size_dip(gfx::ToRoundedSize(
-      gfx::ScaleSize(viewport_size_dip, scale)));
-
-  if (snapshot_size_dip.width() > 0 && snapshot_size_dip.height() > 0) {
-    view->CopyFromSurface(
-        gfx::Rect(), snapshot_size_dip,
-        base::Bind(&PageHandler::ScreencastFrameCaptured,
-                   weak_factory_.GetWeakPtr(),
-                   base::Passed(last_compositor_frame_metadata_.Clone())),
-        kN32_SkColorType);
-    frames_in_flight_++;
-  }
+  // Request a copy of the surface as a scaled SkBitmap.
+  view->CopyFromSurface(
+      gfx::Rect(), snapshot_size,
+      // TODO(crbug/759310): This should be BindOnce.
+      base::BindRepeating(&PageHandler::ScreencastFrameCaptured,
+                          weak_factory_.GetWeakPtr(),
+                          base::Passed(&page_metadata)),
+      kN32_SkColorType);
+  frames_in_flight_++;
 }
 
-void PageHandler::ScreencastFrameCaptured(viz::CompositorFrameMetadata metadata,
-                                          const SkBitmap& bitmap,
-                                          ReadbackResponse response) {
+void PageHandler::ScreencastFrameCaptured(
+    std::unique_ptr<Page::ScreencastFrameMetadata> page_metadata,
+    const SkBitmap& bitmap,
+    ReadbackResponse response) {
   if (response != READBACK_SUCCESS) {
     if (capture_retry_count_) {
       --capture_retry_count_;
@@ -872,50 +882,21 @@
   }
   base::PostTaskWithTraitsAndReplyWithResult(
       FROM_HERE, {base::TaskShutdownBehavior::CONTINUE_ON_SHUTDOWN},
-      base::Bind(&EncodeSkBitmap, bitmap, screencast_format_,
-                 screencast_quality_),
-      base::Bind(&PageHandler::ScreencastFrameEncoded,
-                 weak_factory_.GetWeakPtr(), base::Passed(&metadata),
-                 base::Time::Now()));
+      base::BindOnce(&EncodeSkBitmap, bitmap, screencast_format_,
+                     screencast_quality_),
+      base::BindOnce(&PageHandler::ScreencastFrameEncoded,
+                     weak_factory_.GetWeakPtr(), std::move(page_metadata)));
 }
 
-void PageHandler::ScreencastFrameEncoded(viz::CompositorFrameMetadata metadata,
-                                         const base::Time& timestamp,
-                                         const std::string& data) {
-  // Consider metadata empty in case it has no device scale factor.
-  if (metadata.device_scale_factor == 0 || !host_ || data.empty()) {
+void PageHandler::ScreencastFrameEncoded(
+    std::unique_ptr<Page::ScreencastFrameMetadata> page_metadata,
+    const std::string& data) {
+  if (data.empty()) {
     --frames_in_flight_;
-    return;
+    return;  // Encode failed.
   }
 
-  RenderWidgetHostViewBase* view = static_cast<RenderWidgetHostViewBase*>(
-      host_->GetView());
-  if (!view) {
-    --frames_in_flight_;
-    return;
-  }
-
-  gfx::SizeF screen_size_dip =
-      gfx::ScaleSize(gfx::SizeF(view->GetPhysicalBackingSize()),
-                     1 / metadata.device_scale_factor);
-  float css_to_dip = metadata.page_scale_factor;
-  float top_offset_dip =
-      metadata.top_controls_height * metadata.top_controls_shown_ratio;
-  if (IsUseZoomForDSFEnabled()) {
-    css_to_dip /= metadata.device_scale_factor;
-    top_offset_dip /= metadata.device_scale_factor;
-  }
-  std::unique_ptr<Page::ScreencastFrameMetadata> param_metadata =
-      Page::ScreencastFrameMetadata::Create()
-          .SetPageScaleFactor(css_to_dip)
-          .SetOffsetTop(top_offset_dip)
-          .SetDeviceWidth(screen_size_dip.width())
-          .SetDeviceHeight(screen_size_dip.height())
-          .SetScrollOffsetX(metadata.root_scroll_offset.x())
-          .SetScrollOffsetY(metadata.root_scroll_offset.y())
-          .SetTimestamp(timestamp.ToDoubleT())
-          .Build();
-  frontend_->ScreencastFrame(data, std::move(param_metadata), session_id_);
+  frontend_->ScreencastFrame(data, std::move(page_metadata), session_id_);
 }
 
 void PageHandler::ScreenshotCaptured(
diff --git a/content/browser/devtools/protocol/page_handler.h b/content/browser/devtools/protocol/page_handler.h
index b775b58..1c4d28f 100644
--- a/content/browser/devtools/protocol/page_handler.h
+++ b/content/browser/devtools/protocol/page_handler.h
@@ -153,12 +153,13 @@
   WebContentsImpl* GetWebContents();
   void NotifyScreencastVisibility(bool visible);
   void InnerSwapCompositorFrame();
-  void ScreencastFrameCaptured(viz::CompositorFrameMetadata metadata,
-                               const SkBitmap& bitmap,
-                               ReadbackResponse response);
-  void ScreencastFrameEncoded(viz::CompositorFrameMetadata metadata,
-                              const base::Time& timestamp,
-                              const std::string& data);
+  void ScreencastFrameCaptured(
+      std::unique_ptr<Page::ScreencastFrameMetadata> metadata,
+      const SkBitmap& bitmap,
+      ReadbackResponse response);
+  void ScreencastFrameEncoded(
+      std::unique_ptr<Page::ScreencastFrameMetadata> metadata,
+      const std::string& data);
 
   void ScreenshotCaptured(
       std::unique_ptr<CaptureScreenshotCallback> callback,
diff --git a/content/browser/frame_host/cross_process_frame_connector.cc b/content/browser/frame_host/cross_process_frame_connector.cc
index 351a015d..17e8a8c 100644
--- a/content/browser/frame_host/cross_process_frame_connector.cc
+++ b/content/browser/frame_host/cross_process_frame_connector.cc
@@ -25,6 +25,7 @@
 #include "content/public/common/use_zoom_for_dsf_policy.h"
 #include "gpu/ipc/common/gpu_messages.h"
 #include "third_party/WebKit/public/platform/WebInputEvent.h"
+#include "ui/base/ui_base_features.h"
 #include "ui/base/ui_base_switches_util.h"
 #include "ui/gfx/geometry/dip_util.h"
 
@@ -99,7 +100,7 @@
     if (is_hidden_)
       OnVisibilityChanged(false);
     FrameMsg_ViewChanged_Params params;
-    if (!switches::IsMusHostingViz())
+    if (!base::FeatureList::IsEnabled(::features::kMash))
       params.frame_sink_id = view_->GetFrameSinkId();
     frame_proxy_in_parent_renderer_->Send(new FrameMsg_ViewChanged(
         frame_proxy_in_parent_renderer_->GetRoutingID(), params));
diff --git a/content/browser/frame_host/navigation_controller_impl_unittest.cc b/content/browser/frame_host/navigation_controller_impl_unittest.cc
index e4d999a..0fe642c 100644
--- a/content/browser/frame_host/navigation_controller_impl_unittest.cc
+++ b/content/browser/frame_host/navigation_controller_impl_unittest.cc
@@ -4359,6 +4359,15 @@
   EXPECT_EQ(url5, controller.GetEntryAtIndex(2)->GetURL());
   EXPECT_TRUE(controller.CanGoBack());
 
+  // Deleting the currently commited entry should do nothing.
+  controller.DeleteNavigationEntries(
+      base::BindLambdaForTesting([&](const content::NavigationEntry& entry) {
+        return entry.GetURL() == url5;
+      }));
+  EXPECT_EQ(1U, navigation_entries_deleted_counter_);
+  ASSERT_EQ(3, controller.GetEntryCount());
+  ASSERT_EQ(2, controller.GetCurrentEntryIndex());
+
   // Delete url1 and url3.
   contents()->ExpectSetHistoryOffsetAndLength(0, 1);
   controller.DeleteNavigationEntries(base::BindRepeating(
diff --git a/content/browser/frame_host/render_frame_host_delegate.h b/content/browser/frame_host/render_frame_host_delegate.h
index 1a51932..d4241cf 100644
--- a/content/browser/frame_host/render_frame_host_delegate.h
+++ b/content/browser/frame_host/render_frame_host_delegate.h
@@ -117,7 +117,6 @@
   virtual void RunJavaScriptDialog(RenderFrameHost* render_frame_host,
                                    const base::string16& message,
                                    const base::string16& default_prompt,
-                                   const GURL& frame_url,
                                    JavaScriptDialogType type,
                                    IPC::Message* reply_msg) {}
 
diff --git a/content/browser/frame_host/render_frame_host_impl.cc b/content/browser/frame_host/render_frame_host_impl.cc
index 5956e65..1d0ce56 100644
--- a/content/browser/frame_host/render_frame_host_impl.cc
+++ b/content/browser/frame_host/render_frame_host_impl.cc
@@ -2050,8 +2050,14 @@
   // While a JS message dialog is showing, tabs in the same process shouldn't
   // process input events.
   GetProcess()->SetIgnoreInputEvents(true);
-  delegate_->RunJavaScriptDialog(this, message, default_prompt, frame_url,
-                                 dialog_type, reply_msg);
+
+  // TODO(nasko): It is strange to accept the frame URL as a parameter from
+  // the renderer. Investigate and remove parameter, but for now let's
+  // double check.
+  DCHECK_EQ(frame_url, last_committed_url_);
+
+  delegate_->RunJavaScriptDialog(this, message, default_prompt, dialog_type,
+                                 reply_msg);
 }
 
 void RenderFrameHostImpl::OnRunBeforeUnloadConfirm(
diff --git a/content/browser/frame_host/render_frame_host_impl_browsertest.cc b/content/browser/frame_host/render_frame_host_impl_browsertest.cc
index 7c8f87e3..edc7581 100644
--- a/content/browser/frame_host/render_frame_host_impl_browsertest.cc
+++ b/content/browser/frame_host/render_frame_host_impl_browsertest.cc
@@ -212,7 +212,7 @@
   // JavaScriptDialogManager
 
   void RunJavaScriptDialog(WebContents* web_contents,
-                           const GURL& alerting_frame_url,
+                           RenderFrameHost* render_frame_host,
                            JavaScriptDialogType dialog_type,
                            const base::string16& message_text,
                            const base::string16& default_prompt_text,
diff --git a/content/browser/frame_host/render_widget_host_view_guest.cc b/content/browser/frame_host/render_widget_host_view_guest.cc
index 9bab81a..e84ab8b 100644
--- a/content/browser/frame_host/render_widget_host_view_guest.cc
+++ b/content/browser/frame_host/render_widget_host_view_guest.cc
@@ -18,7 +18,6 @@
 #include "components/viz/service/surfaces/surface_manager.h"
 #include "content/browser/browser_plugin/browser_plugin_guest.h"
 #include "content/browser/compositor/surface_utils.h"
-#include "content/browser/mus_util.h"
 #include "content/browser/renderer_host/cursor_manager.h"
 #include "content/browser/renderer_host/input/input_router.h"
 #include "content/browser/renderer_host/render_view_host_impl.h"
@@ -32,6 +31,7 @@
 #include "content/public/common/use_zoom_for_dsf_policy.h"
 #include "gpu/ipc/common/gpu_messages.h"
 #include "skia/ext/platform_canvas.h"
+#include "ui/base/ui_base_features.h"
 #include "ui/base/ui_base_switches_util.h"
 #include "ui/events/base_event_utils.h"
 #include "ui/gfx/geometry/dip_util.h"
@@ -397,7 +397,7 @@
 void RenderWidgetHostViewGuest::OnAttached() {
   RegisterFrameSinkId();
 #if defined(USE_AURA)
-  if (switches::IsMusHostingViz()) {
+  if (base::FeatureList::IsEnabled(::features::kMash)) {
     aura::Env::GetInstance()->ScheduleEmbed(
         GetWindowTreeClientFromRenderer(),
         base::BindOnce(&RenderWidgetHostViewGuest::OnGotEmbedToken,
diff --git a/content/browser/frame_host/render_widget_host_view_guest_unittest.cc b/content/browser/frame_host/render_widget_host_view_guest_unittest.cc
index 768cdfb..b4e1ad71 100644
--- a/content/browser/frame_host/render_widget_host_view_guest_unittest.cc
+++ b/content/browser/frame_host/render_widget_host_view_guest_unittest.cc
@@ -32,6 +32,7 @@
 #include "content/test/test_render_view_host.h"
 #include "content/test/test_web_contents.h"
 #include "testing/gtest/include/gtest/gtest.h"
+#include "ui/base/ui_base_features.h"
 #include "ui/compositor/compositor.h"
 
 namespace content {
@@ -236,6 +237,10 @@
 }  // anonymous namespace
 
 TEST_F(RenderWidgetHostViewGuestSurfaceTest, TestGuestSurface) {
+  // TODO: fix for mash.
+  if (base::FeatureList::IsEnabled(features::kMash))
+    return;
+
   gfx::Size view_size(100, 100);
   gfx::Rect view_rect(view_size);
   float scale_factor = 1.f;
diff --git a/content/browser/gpu/browser_gpu_channel_host_factory.cc b/content/browser/gpu/browser_gpu_channel_host_factory.cc
index 50f37a2..b2a89383 100644
--- a/content/browser/gpu/browser_gpu_channel_host_factory.cc
+++ b/content/browser/gpu/browser_gpu_channel_host_factory.cc
@@ -19,7 +19,6 @@
 #include "content/browser/gpu/gpu_data_manager_impl.h"
 #include "content/browser/gpu/gpu_process_host.h"
 #include "content/browser/gpu/shader_cache_factory.h"
-#include "content/browser/mus_util.h"
 #include "content/common/child_process_host_impl.h"
 #include "content/public/browser/browser_thread.h"
 #include "content/public/browser/content_browser_client.h"
@@ -29,7 +28,7 @@
 #include "gpu/command_buffer/service/gpu_switches.h"
 #include "services/resource_coordinator/public/mojom/memory_instrumentation/constants.mojom.h"
 #include "services/service_manager/runner/common/client_util.h"
-#include "ui/base/ui_base_switches_util.h"
+#include "ui/base/ui_base_features.h"
 
 namespace content {
 
@@ -274,7 +273,7 @@
 void BrowserGpuChannelHostFactory::EstablishGpuChannel(
     gpu::GpuChannelEstablishedCallback callback) {
 #if defined(USE_AURA)
-  DCHECK(!switches::IsMusHostingViz());
+  DCHECK(!base::FeatureList::IsEnabled(::features::kMash));
 #endif
   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
   if (gpu_channel_.get() && gpu_channel_->IsLost()) {
diff --git a/content/browser/loader/cross_site_document_resource_handler.cc b/content/browser/loader/cross_site_document_resource_handler.cc
index ea9c678..cdd0605 100644
--- a/content/browser/loader/cross_site_document_resource_handler.cc
+++ b/content/browser/loader/cross_site_document_resource_handler.cc
@@ -88,6 +88,7 @@
   recorder->UpdateSourceURL(source_id, web_contents->GetLastCommittedURL());
   ukm::builders::SiteIsolation_XSD_Browser_Blocked(source_id)
       .SetCanonicalMimeType(canonical_mime_type)
+      .SetContentLengthWasZero(content_length == 0)
       .SetContentResourceType(resource_type)
       .SetHttpResponseCode(http_response_code)
       .SetNeededSniffing(needed_sniffing)
diff --git a/content/browser/mus_util.cc b/content/browser/mus_util.cc
deleted file mode 100644
index 5f45f248..0000000
--- a/content/browser/mus_util.cc
+++ /dev/null
@@ -1,21 +0,0 @@
-// Copyright 2017 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "content/browser/mus_util.h"
-
-#if defined(USE_AURA)
-#include "ui/aura/env.h"
-#endif
-
-namespace content {
-
-bool IsUsingMus() {
-#if defined(USE_AURA)
-  return aura::Env::GetInstance()->mode() == aura::Env::Mode::MUS;
-#else
-  return false;
-#endif
-}
-
-}  // namespace content
diff --git a/content/browser/mus_util.h b/content/browser/mus_util.h
deleted file mode 100644
index 4b7c78d3c..0000000
--- a/content/browser/mus_util.h
+++ /dev/null
@@ -1,16 +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 CONTENT_BROWSER_MUS_UTIL_H_
-#define CONTENT_BROWSER_MUS_UTIL_H_
-
-#include "content/common/content_export.h"
-
-namespace content {
-
-CONTENT_EXPORT bool IsUsingMus();
-
-}  // namespace content
-
-#endif  // CONTENT_BROWSER_MUS_UTIL_H_
diff --git a/content/browser/net_info_browsertest.cc b/content/browser/net_info_browsertest.cc
index fea7989f..1ef2c65 100644
--- a/content/browser/net_info_browsertest.cc
+++ b/content/browser/net_info_browsertest.cc
@@ -22,7 +22,6 @@
 #include "net/dns/mock_host_resolver.h"
 #include "net/log/test_net_log.h"
 #include "net/nqe/effective_connection_type.h"
-#include "net/nqe/external_estimate_provider.h"
 #include "net/nqe/network_quality_estimator_test_util.h"
 
 namespace {
@@ -221,7 +220,7 @@
                        NetworkQualityEstimatorNotInitialized) {
   base::HistogramTester histogram_tester;
   net::TestNetworkQualityEstimator estimator(
-      nullptr, std::map<std::string, std::string>(), false, false, true,
+      std::map<std::string, std::string>(), false, false, true,
       std::make_unique<net::BoundTestNetLog>());
   NetworkQualityObserverImpl impl(&estimator);
 
@@ -241,7 +240,7 @@
                        EffectiveConnectionTypeChangeNotfied) {
   base::HistogramTester histogram_tester;
   net::TestNetworkQualityEstimator estimator(
-      nullptr, std::map<std::string, std::string>(), false, false, true,
+      std::map<std::string, std::string>(), false, false, true,
       std::make_unique<net::BoundTestNetLog>());
   NetworkQualityObserverImpl impl(&estimator);
 
@@ -283,7 +282,7 @@
 IN_PROC_BROWSER_TEST_F(NetInfoBrowserTest, NetworkQualityChangeNotified) {
   base::HistogramTester histogram_tester;
   net::TestNetworkQualityEstimator estimator(
-      nullptr, std::map<std::string, std::string>(), false, false, true,
+      std::map<std::string, std::string>(), false, false, true,
       std::make_unique<net::BoundTestNetLog>());
   NetworkQualityObserverImpl impl(&estimator);
 
@@ -320,7 +319,6 @@
 IN_PROC_BROWSER_TEST_F(NetInfoBrowserTest, NetworkQualityChangeRounded) {
   base::HistogramTester histogram_tester;
   net::TestNetworkQualityEstimator estimator(
-      std::unique_ptr<net::ExternalEstimateProvider>(),
       std::map<std::string, std::string>(), false, false, true,
       std::make_unique<net::BoundTestNetLog>());
   NetworkQualityObserverImpl impl(&estimator);
@@ -365,7 +363,6 @@
 IN_PROC_BROWSER_TEST_F(NetInfoBrowserTest, NetworkQualityChangeUpperLimit) {
   base::HistogramTester histogram_tester;
   net::TestNetworkQualityEstimator estimator(
-      std::unique_ptr<net::ExternalEstimateProvider>(),
       std::map<std::string, std::string>(), false, false, true,
       std::make_unique<net::BoundTestNetLog>());
   NetworkQualityObserverImpl impl(&estimator);
@@ -387,7 +384,6 @@
 IN_PROC_BROWSER_TEST_F(NetInfoBrowserTest, NetworkQualityRandomized) {
   base::HistogramTester histogram_tester;
   net::TestNetworkQualityEstimator estimator(
-      std::unique_ptr<net::ExternalEstimateProvider>(),
       std::map<std::string, std::string>(), false, false, true,
       std::make_unique<net::BoundTestNetLog>());
   NetworkQualityObserverImpl impl(&estimator);
@@ -449,7 +445,7 @@
 IN_PROC_BROWSER_TEST_F(NetInfoBrowserTest, NetworkQualityChangeNotNotified) {
   base::HistogramTester histogram_tester;
   net::TestNetworkQualityEstimator estimator(
-      nullptr, std::map<std::string, std::string>(), false, false, true,
+      std::map<std::string, std::string>(), false, false, true,
       std::make_unique<net::BoundTestNetLog>());
   NetworkQualityObserverImpl impl(&estimator);
 
diff --git a/content/browser/notifications/blink_notification_service_impl.cc b/content/browser/notifications/blink_notification_service_impl.cc
index 6d1f390..81d48de 100644
--- a/content/browser/notifications/blink_notification_service_impl.cc
+++ b/content/browser/notifications/blink_notification_service_impl.cc
@@ -145,4 +145,39 @@
       resource_context_, origin_.GetURL(), render_process_id_);
 }
 
+void BlinkNotificationServiceImpl::DisplayPersistentNotification(
+    int64_t service_worker_registration_id,
+    const PlatformNotificationData& platform_notification_data,
+    const NotificationResources& notification_resources,
+    DisplayPersistentNotificationCallback callback) {
+  DCHECK_CURRENTLY_ON(BrowserThread::IO);
+  if (!Service()) {
+    std::move(callback).Run(
+        blink::mojom::PersistentNotificationError::INTERNAL_ERROR);
+    return;
+  }
+  if (CheckPermissionStatus() != blink::mojom::PermissionStatus::GRANTED) {
+    std::move(callback).Run(
+        blink::mojom::PersistentNotificationError::PERMISSION_DENIED);
+    return;
+  }
+
+  // TODO(https://crbug.com/796991): Write notification data to the database,
+  // and get back a real notification ID to use here.
+  std::string notification_id = "FIXME";
+
+  // Using base::Unretained here is safe because Service() returns a singleton.
+  // TODO(https://crbug.com/796991): Get service worker registration from its
+  // ID, and pass the service worker scope (instead of the origin twice) below.
+  BrowserThread::PostTask(
+      BrowserThread::UI, FROM_HERE,
+      base::BindOnce(
+          &PlatformNotificationService::DisplayPersistentNotification,
+          base::Unretained(Service()), browser_context_, notification_id,
+          origin_.GetURL() /* service_worker_scope */,
+          origin_.GetURL() /* origin */, platform_notification_data,
+          notification_resources));
+  std::move(callback).Run(blink::mojom::PersistentNotificationError::NONE);
+}
+
 }  // namespace content
diff --git a/content/browser/notifications/blink_notification_service_impl.h b/content/browser/notifications/blink_notification_service_impl.h
index 2adae5a..32ea77d 100644
--- a/content/browser/notifications/blink_notification_service_impl.h
+++ b/content/browser/notifications/blink_notification_service_impl.h
@@ -43,6 +43,11 @@
       const NotificationResources& notification_resources,
       blink::mojom::NonPersistentNotificationListenerPtr listener_ptr) override;
   void CloseNonPersistentNotification(const std::string& token) override;
+  void DisplayPersistentNotification(
+      int64_t service_worker_registration_id,
+      const PlatformNotificationData& platform_notification_data,
+      const NotificationResources& notification_resources,
+      DisplayPersistentNotificationCallback) override;
 
  private:
   // Called when an error is detected on binding_.
diff --git a/content/browser/notifications/blink_notification_service_impl_unittest.cc b/content/browser/notifications/blink_notification_service_impl_unittest.cc
index b0b9610..a702a12 100644
--- a/content/browser/notifications/blink_notification_service_impl_unittest.cc
+++ b/content/browser/notifications/blink_notification_service_impl_unittest.cc
@@ -33,6 +33,7 @@
 
 const int kFakeRenderProcessId = 1;
 const char kTestOrigin[] = "https://example.com";
+const int64_t kFakeServiceWorkerRegistrationId = 1234;
 
 class MockNonPersistentNotificationListener
     : public blink::mojom::NonPersistentNotificationListener {
@@ -107,6 +108,11 @@
     return permission_callback_result_;
   }
 
+  void DidDisplayPersistentNotification(
+      blink::mojom::PersistentNotificationError error) {
+    display_persistent_callback_result_ = error;
+  }
+
   void DidGetDisplayedNotifications(
       base::OnceClosure quit_closure,
       std::unique_ptr<std::set<std::string>> notification_ids,
@@ -139,6 +145,8 @@
 
   MockNonPersistentNotificationListener non_persistent_notification_listener_;
 
+  blink::mojom::PersistentNotificationError display_persistent_callback_result_;
+
  private:
   NotificationBrowserClient notification_browser_client_;
 
@@ -215,4 +223,43 @@
   EXPECT_EQ(0u, GetDisplayedNotifications().size());
 }
 
+TEST_F(BlinkNotificationServiceImplTest,
+       DisplayPersistentNotificationWithPermission) {
+  mock_platform_service_.SetPermission(blink::mojom::PermissionStatus::GRANTED);
+
+  notification_service_->DisplayPersistentNotification(
+      kFakeServiceWorkerRegistrationId, PlatformNotificationData(),
+      NotificationResources(),
+      base::BindOnce(
+          &BlinkNotificationServiceImplTest::DidDisplayPersistentNotification,
+          base::Unretained(this)));
+  EXPECT_EQ(blink::mojom::PersistentNotificationError::NONE,
+            display_persistent_callback_result_);
+
+  // Wait for service to receive the Display call.
+  RunAllTasksUntilIdle();
+
+  EXPECT_EQ(1u, GetDisplayedNotifications().size());
+}
+
+TEST_F(BlinkNotificationServiceImplTest,
+       DisplayPersistentNotificationWithoutPermission) {
+  mock_platform_service_.SetPermission(blink::mojom::PermissionStatus::DENIED);
+
+  notification_service_->DisplayPersistentNotification(
+      kFakeServiceWorkerRegistrationId, PlatformNotificationData(),
+      NotificationResources(),
+      base::BindOnce(
+          &BlinkNotificationServiceImplTest::DidDisplayPersistentNotification,
+          base::Unretained(this)));
+
+  EXPECT_EQ(blink::mojom::PersistentNotificationError::PERMISSION_DENIED,
+            display_persistent_callback_result_);
+
+  // Give Service a chance to receive any unexpected Display calls.
+  RunAllTasksUntilIdle();
+
+  EXPECT_EQ(0u, GetDisplayedNotifications().size());
+}
+
 }  // namespace content
diff --git a/content/browser/oop_browsertest.cc b/content/browser/oop_browsertest.cc
index aadcb33..8b58305 100644
--- a/content/browser/oop_browsertest.cc
+++ b/content/browser/oop_browsertest.cc
@@ -18,6 +18,7 @@
 #include "gpu/config/gpu_switches.h"
 #include "third_party/skia/include/core/SkBitmap.h"
 #include "third_party/skia/include/core/SkColor.h"
+#include "ui/base/ui_base_features.h"
 #include "ui/base/ui_base_switches.h"
 #include "ui/compositor/compositor_switches.h"
 #include "ui/gl/gl_switches.h"
@@ -33,10 +34,7 @@
     command_line->AppendSwitch(switches::kEnablePixelOutputInTests);
     command_line->AppendSwitch(switches::kEnableOOPRasterization);
 
-    bool use_gpu_in_tests = true;
-#if defined(USE_AURA)
-    use_gpu_in_tests = !command_line->HasSwitch(switches::kMus);
-#endif
+    const bool use_gpu_in_tests = !features::IsMusEnabled();
     if (use_gpu_in_tests)
       command_line->AppendSwitch(switches::kUseGpuInTests);
   }
diff --git a/content/browser/payments/payment_instrument_icon_fetcher.cc b/content/browser/payments/payment_instrument_icon_fetcher.cc
index de37dbfd..e2d68392 100644
--- a/content/browser/payments/payment_instrument_icon_fetcher.cc
+++ b/content/browser/payments/payment_instrument_icon_fetcher.cc
@@ -72,6 +72,15 @@
   GURL icon_url = ManifestIconSelector::FindBestMatchingIcon(
       icons, kPaymentAppIdealIconSize, kPaymentAppMinimumIconSize,
       Manifest::Icon::IconPurpose::ANY);
+  if (web_contents == nullptr || !icon_url.is_valid()) {
+    // If the icon url is invalid, it's better to give the information to
+    // developers in advance unlike when fetching or decoding fails. We already
+    // checked whether they are valid in renderer side. So, if the icon url is
+    // invalid, it's something wrong.
+    BrowserThread::PostTask(BrowserThread::IO, FROM_HERE,
+                            base::BindOnce(std::move(callback), std::string()));
+    return;
+  }
 
   std::vector<Manifest::Icon> copy_icons;
   for (const auto& icon : icons) {
@@ -85,14 +94,7 @@
       kPaymentAppMinimumIconSize,
       base::Bind(&OnIconFetched, web_contents, copy_icons,
                  base::Passed(std::move(callback))));
-  // If the icon url is invalid, it's better to give the information to
-  // developers in advance unlike when fetching or decoding fails.
-  // We already checked whether they are valid in renderer side. So, if the
-  // icon url is invalid, it's something wrong.
-  if (!can_download_icon) {
-    BrowserThread::PostTask(BrowserThread::IO, FROM_HERE,
-                            base::BindOnce(std::move(callback), std::string()));
-  }
+  DCHECK(can_download_icon);
 }
 
 WebContents* GetWebContentsFromProviderHostIds(
diff --git a/content/browser/ppapi_plugin_process_host.cc b/content/browser/ppapi_plugin_process_host.cc
index c58d0268..420f5e14 100644
--- a/content/browser/ppapi_plugin_process_host.cc
+++ b/content/browser/ppapi_plugin_process_host.cc
@@ -371,10 +371,6 @@
 #if defined(OS_MACOSX)
       switches::kEnableSandboxLogging,
 #endif
-#if defined(USE_AURA)
-      switches::kMus,
-      switches::kMusHostingViz,
-#endif
       switches::kNoSandbox,
       switches::kPpapiStartupDialog,
     };
diff --git a/content/browser/renderer_host/input/motion_event_web.cc b/content/browser/renderer_host/input/motion_event_web.cc
index 1c2074c9..1e9290c 100644
--- a/content/browser/renderer_host/input/motion_event_web.cc
+++ b/content/browser/renderer_host/input/motion_event_web.cc
@@ -24,27 +24,27 @@
     case WebInputEvent::kTouchStart:
       if (WebTouchEventTraits::AllTouchPointsHaveState(
               event, WebTouchPoint::kStatePressed))
-        return ui::MotionEvent::ACTION_DOWN;
+        return ui::MotionEvent::Action::DOWN;
       else
-        return ui::MotionEvent::ACTION_POINTER_DOWN;
+        return ui::MotionEvent::Action::POINTER_DOWN;
     case WebInputEvent::kTouchEnd:
       if (WebTouchEventTraits::AllTouchPointsHaveState(
               event, WebTouchPoint::kStateReleased))
-        return ui::MotionEvent::ACTION_UP;
+        return ui::MotionEvent::Action::UP;
       else
-        return ui::MotionEvent::ACTION_POINTER_UP;
+        return ui::MotionEvent::Action::POINTER_UP;
     case WebInputEvent::kTouchCancel:
       DCHECK(WebTouchEventTraits::AllTouchPointsHaveState(
           event, WebTouchPoint::kStateCancelled));
-      return ui::MotionEvent::ACTION_CANCEL;
+      return ui::MotionEvent::Action::CANCEL;
     case WebInputEvent::kTouchMove:
-      return ui::MotionEvent::ACTION_MOVE;
+      return ui::MotionEvent::Action::MOVE;
     default:
       break;
   };
   NOTREACHED()
       << "Unable to derive a valid MotionEvent::Action from the WebTouchEvent.";
-  return ui::MotionEvent::ACTION_CANCEL;
+  return ui::MotionEvent::Action::CANCEL;
 }
 
 int GetActionIndexFrom(const WebTouchEvent& event) {
@@ -77,8 +77,8 @@
 }
 
 int MotionEventWeb::GetActionIndex() const {
-  DCHECK(cached_action_ == ACTION_POINTER_UP ||
-         cached_action_ == ACTION_POINTER_DOWN)
+  DCHECK(cached_action_ == Action::POINTER_UP ||
+         cached_action_ == Action::POINTER_DOWN)
       << "Invalid action for GetActionIndex(): " << cached_action_;
   DCHECK_GE(cached_action_index_, 0);
   DCHECK_LT(cached_action_index_, static_cast<int>(event_.touches_length));
@@ -134,7 +134,7 @@
   DCHECK(0 <= orientation_rad && orientation_rad <= base::kPiFloat / 2)
       << "Unexpected touch rotation angle";
 
-  if (GetToolType(pointer_index) == TOOL_TYPE_STYLUS) {
+  if (GetToolType(pointer_index) == ToolType::STYLUS) {
     const WebPointerProperties& pointer = event_.touches[pointer_index];
 
     if (pointer.tilt_y <= 0 && pointer.tilt_x < 0) {
@@ -168,7 +168,7 @@
 float MotionEventWeb::GetTiltX(size_t pointer_index) const {
   DCHECK_LT(pointer_index, GetPointerCount());
 
-  if (GetToolType(pointer_index) != TOOL_TYPE_STYLUS)
+  if (GetToolType(pointer_index) != ToolType::STYLUS)
     return 0.f;
 
   return event_.touches[pointer_index].tilt_x;
@@ -177,7 +177,7 @@
 float MotionEventWeb::GetTiltY(size_t pointer_index) const {
   DCHECK_LT(pointer_index, GetPointerCount());
 
-  if (GetToolType(pointer_index) != TOOL_TYPE_STYLUS)
+  if (GetToolType(pointer_index) != ToolType::STYLUS)
     return 0.f;
 
   return event_.touches[pointer_index].tilt_y;
@@ -197,18 +197,18 @@
 
   switch (pointer.pointer_type) {
     case WebPointerProperties::PointerType::kUnknown:
-      return TOOL_TYPE_UNKNOWN;
+      return ToolType::UNKNOWN;
     case WebPointerProperties::PointerType::kMouse:
-      return TOOL_TYPE_MOUSE;
+      return ToolType::MOUSE;
     case WebPointerProperties::PointerType::kPen:
-      return TOOL_TYPE_STYLUS;
+      return ToolType::STYLUS;
     case WebPointerProperties::PointerType::kEraser:
-      return TOOL_TYPE_ERASER;
+      return ToolType::ERASER;
     case WebPointerProperties::PointerType::kTouch:
-      return TOOL_TYPE_FINGER;
+      return ToolType::FINGER;
   }
   NOTREACHED() << "Unexpected pointerType";
-  return TOOL_TYPE_UNKNOWN;
+  return ToolType::UNKNOWN;
 }
 
 int MotionEventWeb::GetButtonState() const {
diff --git a/content/browser/renderer_host/input/motion_event_web_unittest.cc b/content/browser/renderer_host/input/motion_event_web_unittest.cc
index 1074e92..ddd36fee 100644
--- a/content/browser/renderer_host/input/motion_event_web_unittest.cc
+++ b/content/browser/renderer_host/input/motion_event_web_unittest.cc
@@ -22,13 +22,13 @@
   const float orientations[] = {-pi, -2.f * pi / 3, -pi / 2};
   const float tilts_x[] = {0.f, -180 / 4, -180 / 3};
   const float tilts_y[] = {0.5f, 180 / 2, 180 / 3};
-  const MotionEvent::ToolType tool_types[] = {MotionEvent::TOOL_TYPE_FINGER,
-                                              MotionEvent::TOOL_TYPE_STYLUS,
-                                              MotionEvent::TOOL_TYPE_MOUSE};
+  const MotionEvent::ToolType tool_types[] = {MotionEvent::ToolType::FINGER,
+                                              MotionEvent::ToolType::STYLUS,
+                                              MotionEvent::ToolType::MOUSE};
 
   base::TimeTicks event_time = base::TimeTicks::Now();
   PointerProperties pp;
-  MotionEventGeneric generic_event(MotionEvent::ACTION_MOVE, event_time, pp);
+  MotionEventGeneric generic_event(MotionEvent::Action::MOVE, event_time, pp);
   for (MotionEvent::ToolType tool_type : tool_types) {
     for (size_t i = 0; i < arraysize(tilts_x); ++i) {
       const float tilt_x = tilts_x[i];
@@ -47,7 +47,7 @@
 
       MotionEventWeb event(web_touch_event);
       EXPECT_EQ(tool_type, event.GetToolType(pointer_index));
-      if (tool_type == MotionEvent::TOOL_TYPE_STYLUS) {
+      if (tool_type == MotionEvent::ToolType::STYLUS) {
         // Web touch event touch point tilt plane angles are stored as ints,
         // thus the tilt precision is 1 degree and the error should not be
         // greater than 0.5 degrees.
@@ -59,7 +59,7 @@
         EXPECT_EQ(0.f, event.GetTiltX(pointer_index));
         EXPECT_EQ(0.f, event.GetTiltY(pointer_index));
       }
-      if (tool_type == MotionEvent::TOOL_TYPE_STYLUS && tilt_x > 0.f) {
+      if (tool_type == MotionEvent::ToolType::STYLUS && tilt_x > 0.f) {
         // Full stylus tilt orientation information survives above event
         // conversions only if there is a non-zero stylus tilt angle.
         // See: http://crbug.com/251330
diff --git a/content/browser/renderer_host/input/stylus_text_selector.cc b/content/browser/renderer_host/input/stylus_text_selector.cc
index 18285e64..89660b8 100644
--- a/content/browser/renderer_host/input/stylus_text_selector.cc
+++ b/content/browser/renderer_host/input/stylus_text_selector.cc
@@ -51,7 +51,7 @@
 bool StylusTextSelector::OnTouchEvent(const MotionEvent& event) {
   // Only trigger selection on ACTION_DOWN to prevent partial touch or gesture
   // sequences from being forwarded.
-  if (event.GetAction() == MotionEvent::ACTION_DOWN)
+  if (event.GetAction() == MotionEvent::Action::DOWN)
     text_selection_triggered_ = ShouldStartTextSelection(event);
 
   if (!text_selection_triggered_)
@@ -64,13 +64,13 @@
       event.GetButtonState() == MotionEvent::BUTTON_STYLUS_PRIMARY;
 
   switch (event.GetAction()) {
-    case MotionEvent::ACTION_DOWN:
+    case MotionEvent::Action::DOWN:
       drag_state_ = NO_DRAG;
       anchor_x_ = event.GetX();
       anchor_y_ = event.GetY();
       break;
 
-    case MotionEvent::ACTION_MOVE:
+    case MotionEvent::Action::MOVE:
       if (!secondary_button_pressed_) {
         if (drag_state_ == DRAGGING_WITH_BUTTON_PRESSED)
           drag_state_ = DRAGGING_WITH_BUTTON_RELEASED;
@@ -79,23 +79,23 @@
       }
       break;
 
-    case MotionEvent::ACTION_UP:
-    case MotionEvent::ACTION_CANCEL:
+    case MotionEvent::Action::UP:
+    case MotionEvent::Action::CANCEL:
       if (drag_state_ == DRAGGING_WITH_BUTTON_PRESSED ||
           drag_state_ == DRAGGING_WITH_BUTTON_RELEASED)
         client_->OnStylusSelectEnd(event.GetX(), event.GetY());
       drag_state_ = NO_DRAG;
       break;
 
-    case MotionEvent::ACTION_POINTER_UP:
-    case MotionEvent::ACTION_POINTER_DOWN:
+    case MotionEvent::Action::POINTER_UP:
+    case MotionEvent::Action::POINTER_DOWN:
       break;
-    case MotionEvent::ACTION_NONE:
-    case MotionEvent::ACTION_HOVER_ENTER:
-    case MotionEvent::ACTION_HOVER_EXIT:
-    case MotionEvent::ACTION_HOVER_MOVE:
-    case MotionEvent::ACTION_BUTTON_PRESS:
-    case MotionEvent::ACTION_BUTTON_RELEASE:
+    case MotionEvent::Action::NONE:
+    case MotionEvent::Action::HOVER_ENTER:
+    case MotionEvent::Action::HOVER_EXIT:
+    case MotionEvent::Action::HOVER_MOVE:
+    case MotionEvent::Action::BUTTON_PRESS:
+    case MotionEvent::Action::BUTTON_RELEASE:
       NOTREACHED();
       break;
   }
@@ -142,7 +142,7 @@
 bool StylusTextSelector::ShouldStartTextSelection(const MotionEvent& event) {
   DCHECK_GT(event.GetPointerCount(), 0u);
   // Currently we are supporting stylus-only cases.
-  const bool is_stylus = event.GetToolType(0) == MotionEvent::TOOL_TYPE_STYLUS;
+  const bool is_stylus = event.GetToolType(0) == MotionEvent::ToolType::STYLUS;
 
   // For Android version < M, stylus button pressed state is BUTTON_SECONDARY.
   // From Android M, this state has changed to BUTTON_STYLUS_PRIMARY.
diff --git a/content/browser/renderer_host/input/stylus_text_selector_unittest.cc b/content/browser/renderer_host/input/stylus_text_selector_unittest.cc
index 9097947..3b5fad8 100644
--- a/content/browser/renderer_host/input/stylus_text_selector_unittest.cc
+++ b/content/browser/renderer_host/input/stylus_text_selector_unittest.cc
@@ -66,23 +66,23 @@
 TEST_F(StylusTextSelectorTest, ShouldStartTextSelection) {
   base::TimeTicks event_time = base::TimeTicks::Now();
   {  // Touched with a finger.
-    MockMotionEvent e(MotionEvent::ACTION_DOWN, event_time, 50.0f, 50.0f);
-    e.SetToolType(0, MotionEvent::TOOL_TYPE_FINGER);
+    MockMotionEvent e(MotionEvent::Action::DOWN, event_time, 50.0f, 50.0f);
+    e.SetToolType(0, MotionEvent::ToolType::FINGER);
     e.set_button_state(0);
     EXPECT_FALSE(selector_->ShouldStartTextSelection(e));
   }
 
   {  // Touched with a stylus, but no button pressed.
-    MockMotionEvent e(MotionEvent::ACTION_DOWN, event_time, 50.0f, 50.0f);
-    e.SetToolType(0, MotionEvent::TOOL_TYPE_STYLUS);
+    MockMotionEvent e(MotionEvent::Action::DOWN, event_time, 50.0f, 50.0f);
+    e.SetToolType(0, MotionEvent::ToolType::STYLUS);
     e.set_button_state(0);
     EXPECT_FALSE(selector_->ShouldStartTextSelection(e));
   }
 
   {  // Touched with a stylus, with first button (BUTTON_SECONDARY) pressed.
      // For Android version < M, this stylus state is BUTTON_SECONDARY.
-    MockMotionEvent e(MotionEvent::ACTION_DOWN, event_time, 50.0f, 50.0f);
-    e.SetToolType(0, MotionEvent::TOOL_TYPE_STYLUS);
+    MockMotionEvent e(MotionEvent::Action::DOWN, event_time, 50.0f, 50.0f);
+    e.SetToolType(0, MotionEvent::ToolType::STYLUS);
     e.set_button_state(MotionEvent::BUTTON_SECONDARY);
     EXPECT_TRUE(selector_->ShouldStartTextSelection(e));
   }
@@ -90,8 +90,8 @@
   {  // Touched with a stylus, with first button (BUTTON_STYLUS_PRIMARY)
      // pressed. From Android M, this stylus state has been changed to
      // BUTTON_STYLUS_PRIMARY.
-    MockMotionEvent e(MotionEvent::ACTION_DOWN, event_time, 50.0f, 50.0f);
-    e.SetToolType(0, MotionEvent::TOOL_TYPE_STYLUS);
+    MockMotionEvent e(MotionEvent::Action::DOWN, event_time, 50.0f, 50.0f);
+    e.SetToolType(0, MotionEvent::ToolType::STYLUS);
     e.set_button_state(MotionEvent::BUTTON_STYLUS_PRIMARY);
     EXPECT_TRUE(selector_->ShouldStartTextSelection(e));
   }
@@ -99,8 +99,8 @@
   {  // Touched with a stylus, with two buttons pressed.
      // For Android version < M, these states are BUTTON_SECONDARY,
      // BUTTON_TERTIARY.
-    MockMotionEvent e(MotionEvent::ACTION_DOWN, event_time, 50.0f, 50.0f);
-    e.SetToolType(0, MotionEvent::TOOL_TYPE_STYLUS);
+    MockMotionEvent e(MotionEvent::Action::DOWN, event_time, 50.0f, 50.0f);
+    e.SetToolType(0, MotionEvent::ToolType::STYLUS);
     e.set_button_state(MotionEvent::BUTTON_SECONDARY |
                        MotionEvent::BUTTON_TERTIARY);
     EXPECT_FALSE(selector_->ShouldStartTextSelection(e));
@@ -109,8 +109,8 @@
   {  // Touched with a stylus, with two buttons pressed.
      // From Android M, these state are BUTTON_STYLUS_PRIMARY,
      // BUTTON_STYLUS_SECONDARY.
-    MockMotionEvent e(MotionEvent::ACTION_DOWN, event_time, 50.0f, 50.0f);
-    e.SetToolType(0, MotionEvent::TOOL_TYPE_STYLUS);
+    MockMotionEvent e(MotionEvent::Action::DOWN, event_time, 50.0f, 50.0f);
+    e.SetToolType(0, MotionEvent::ToolType::STYLUS);
     e.set_button_state(MotionEvent::BUTTON_STYLUS_PRIMARY |
                        MotionEvent::BUTTON_STYLUS_SECONDARY);
     EXPECT_FALSE(selector_->ShouldStartTextSelection(e));
@@ -122,8 +122,8 @@
   const float x = 50.0f;
   const float y = 30.0f;
   // 1. Touched with a finger: ignored
-  MockMotionEvent finger(MotionEvent::ACTION_DOWN, event_time, x, y);
-  finger.SetToolType(0, MotionEvent::TOOL_TYPE_FINGER);
+  MockMotionEvent finger(MotionEvent::Action::DOWN, event_time, x, y);
+  finger.SetToolType(0, MotionEvent::ToolType::FINGER);
   EXPECT_FALSE(selector_->OnTouchEvent(finger));
   // We do not consume finger events.
   EXPECT_TRUE(event_log_.empty());
@@ -139,35 +139,35 @@
   const float y2 = 90.0f;
   const float x3 = 150.0f;
   const float y3 = 150.0f;
-  // 1. ACTION_DOWN with stylus + button
+  // 1. Action::DOWN with stylus + button
   event_time += base::TimeDelta::FromMilliseconds(10);
-  MockMotionEvent action_down(MotionEvent::ACTION_DOWN, event_time, x1, y1);
-  action_down.SetToolType(0, MotionEvent::TOOL_TYPE_STYLUS);
+  MockMotionEvent action_down(MotionEvent::Action::DOWN, event_time, x1, y1);
+  action_down.SetToolType(0, MotionEvent::ToolType::STYLUS);
   action_down.set_button_state(MotionEvent::BUTTON_SECONDARY);
   EXPECT_TRUE(selector_->OnTouchEvent(action_down));
   EXPECT_TRUE(event_log_.empty());
 
-  // 2. ACTION_MOVE
+  // 2. Action::MOVE
   event_time += base::TimeDelta::FromMilliseconds(10);
-  MockMotionEvent action_move(MotionEvent::ACTION_MOVE, event_time, x2, y2);
-  action_move.SetToolType(0, MotionEvent::TOOL_TYPE_STYLUS);
+  MockMotionEvent action_move(MotionEvent::Action::MOVE, event_time, x2, y2);
+  action_move.SetToolType(0, MotionEvent::ToolType::STYLUS);
   action_move.set_button_state(MotionEvent::BUTTON_SECONDARY);
   EXPECT_TRUE(selector_->OnTouchEvent(action_move));
   ASSERT_EQ(1u, event_log_.size());
   EXPECT_STREQ("Begin(50, 30, 100, 90)", event_log_.back().c_str());
 
   event_time += base::TimeDelta::FromMilliseconds(10);
-  action_move = MockMotionEvent(MotionEvent::ACTION_MOVE, event_time, x3, y3);
-  action_move.SetToolType(0, MotionEvent::TOOL_TYPE_STYLUS);
+  action_move = MockMotionEvent(MotionEvent::Action::MOVE, event_time, x3, y3);
+  action_move.SetToolType(0, MotionEvent::ToolType::STYLUS);
   action_move.set_button_state(MotionEvent::BUTTON_SECONDARY);
   EXPECT_TRUE(selector_->OnTouchEvent(action_move));
   ASSERT_EQ(2u, event_log_.size());
   EXPECT_STREQ("Update(150, 150)", event_log_.back().c_str());
 
-  // 3. ACTION_UP
+  // 3. Action::UP
   event_time += base::TimeDelta::FromMilliseconds(10);
-  MockMotionEvent action_up(MotionEvent::ACTION_UP, event_time, x3, y3);
-  action_up.SetToolType(0, MotionEvent::TOOL_TYPE_STYLUS);
+  MockMotionEvent action_up(MotionEvent::Action::UP, event_time, x3, y3);
+  action_up.SetToolType(0, MotionEvent::ToolType::STYLUS);
   action_up.set_button_state(0);
   EXPECT_TRUE(selector_->OnTouchEvent(action_up));
   ASSERT_EQ(3u, event_log_.size());  // NO CHANGE
@@ -179,60 +179,60 @@
   float x = 50.0f;
   float y = 30.0f;
 
-  // 1. ACTION_DOWN with stylus + button
+  // 1. Action::DOWN with stylus + button
   event_time += base::TimeDelta::FromMilliseconds(10);
-  MockMotionEvent action_down(MotionEvent::ACTION_DOWN, event_time, x, y);
-  action_down.SetToolType(0, MotionEvent::TOOL_TYPE_STYLUS);
+  MockMotionEvent action_down(MotionEvent::Action::DOWN, event_time, x, y);
+  action_down.SetToolType(0, MotionEvent::ToolType::STYLUS);
   action_down.set_button_state(MotionEvent::BUTTON_SECONDARY);
   EXPECT_TRUE(selector_->OnTouchEvent(action_down));
   EXPECT_TRUE(event_log_.empty());
 
-  // 2. ACTION_MOVE
+  // 2. Action::MOVE
   event_time += base::TimeDelta::FromMilliseconds(10);
   x += 20;  // 70
   y += 20;  // 50
-  MockMotionEvent action_move(MotionEvent::ACTION_MOVE, event_time, x, y);
-  action_move.SetToolType(0, MotionEvent::TOOL_TYPE_STYLUS);
+  MockMotionEvent action_move(MotionEvent::Action::MOVE, event_time, x, y);
+  action_move.SetToolType(0, MotionEvent::ToolType::STYLUS);
   action_move.set_button_state(MotionEvent::BUTTON_SECONDARY);
   EXPECT_TRUE(selector_->OnTouchEvent(action_move));
   ASSERT_EQ(1u, event_log_.size());
   EXPECT_STREQ("Begin(50, 30, 70, 50)", event_log_.back().c_str());
 
-  // 3. ACTION_MOVE with stylus + no button
+  // 3. Action::MOVE with stylus + no button
   event_time += base::TimeDelta::FromMilliseconds(10);
   x += 20;  // 90
   y += 20;  // 70
-  action_move = MockMotionEvent(MotionEvent::ACTION_MOVE, event_time, x, y);
-  action_move.SetToolType(0, MotionEvent::TOOL_TYPE_STYLUS);
+  action_move = MockMotionEvent(MotionEvent::Action::MOVE, event_time, x, y);
+  action_move.SetToolType(0, MotionEvent::ToolType::STYLUS);
   action_move.set_button_state(0);
   EXPECT_TRUE(selector_->OnTouchEvent(action_move));
   EXPECT_EQ(1u, event_log_.size());  // NO CHANGE
 
-  // 4. ACTION_MOVE with stylus + button pressed again
+  // 4. Action::MOVE with stylus + button pressed again
   //    Note that the end action is deferred until the stylus is lifted.
   event_time += base::TimeDelta::FromMilliseconds(10);
   x += 20;  // 110
   y += 20;  // 90
-  action_move = MockMotionEvent(MotionEvent::ACTION_MOVE, event_time, x, y);
-  action_move.SetToolType(0, MotionEvent::TOOL_TYPE_STYLUS);
+  action_move = MockMotionEvent(MotionEvent::Action::MOVE, event_time, x, y);
+  action_move.SetToolType(0, MotionEvent::ToolType::STYLUS);
   action_move.set_button_state(MotionEvent::BUTTON_SECONDARY);
   EXPECT_TRUE(selector_->OnTouchEvent(action_move));
   EXPECT_EQ(2u, event_log_.size());
   EXPECT_STREQ("Begin(90, 70, 110, 90)", event_log_.back().c_str());
 
-  // 5. ACTION_MOVE with stylus + no button
+  // 5. Action::MOVE with stylus + no button
   event_time += base::TimeDelta::FromMilliseconds(10);
   x += 20;  // 130
   y += 20;  // 110
-  action_move = MockMotionEvent(MotionEvent::ACTION_MOVE, event_time, x, y);
-  action_move.SetToolType(0, MotionEvent::TOOL_TYPE_STYLUS);
+  action_move = MockMotionEvent(MotionEvent::Action::MOVE, event_time, x, y);
+  action_move.SetToolType(0, MotionEvent::ToolType::STYLUS);
   EXPECT_TRUE(selector_->OnTouchEvent(action_move));
   EXPECT_EQ(2u, event_log_.size());  // NO CHANGE
 
-  // 5. ACTION_UP
+  // 5. Action::UP
   event_time += base::TimeDelta::FromMilliseconds(10);
-  MockMotionEvent action_up(MotionEvent::ACTION_UP, event_time, x, y);
-  action_up.SetToolType(0, MotionEvent::TOOL_TYPE_STYLUS);
+  MockMotionEvent action_up(MotionEvent::Action::UP, event_time, x, y);
+  action_up.SetToolType(0, MotionEvent::ToolType::STYLUS);
   action_up.set_button_state(0);
   EXPECT_TRUE(selector_->OnTouchEvent(action_up));
   EXPECT_EQ(3u, event_log_.size());
@@ -245,26 +245,26 @@
   const float y1 = 30.0f;
   const float x2 = 51.0f;
   const float y2 = 31.0f;
-  // 1. ACTION_DOWN with stylus + button
+  // 1. Action::DOWN with stylus + button
   event_time += base::TimeDelta::FromMilliseconds(1);
-  MockMotionEvent action_down(MotionEvent::ACTION_DOWN, event_time, x1, y1);
-  action_down.SetToolType(0, MotionEvent::TOOL_TYPE_STYLUS);
+  MockMotionEvent action_down(MotionEvent::Action::DOWN, event_time, x1, y1);
+  action_down.SetToolType(0, MotionEvent::ToolType::STYLUS);
   action_down.set_button_state(MotionEvent::BUTTON_SECONDARY);
   EXPECT_TRUE(selector_->OnTouchEvent(action_down));
   EXPECT_TRUE(event_log_.empty());
 
-  // 2. ACTION_MOVE
+  // 2. Action::MOVE
   event_time += base::TimeDelta::FromMilliseconds(1);
-  MockMotionEvent action_move(MotionEvent::ACTION_MOVE, event_time, x2, y2);
-  action_move.SetToolType(0, MotionEvent::TOOL_TYPE_STYLUS);
+  MockMotionEvent action_move(MotionEvent::Action::MOVE, event_time, x2, y2);
+  action_move.SetToolType(0, MotionEvent::ToolType::STYLUS);
   action_move.set_button_state(MotionEvent::BUTTON_SECONDARY);
   EXPECT_TRUE(selector_->OnTouchEvent(action_move));
   EXPECT_TRUE(event_log_.empty());
 
-  // 3. ACTION_UP
+  // 3. Action::UP
   event_time += base::TimeDelta::FromMilliseconds(1);
-  MockMotionEvent action_up(MotionEvent::ACTION_UP, event_time, x2, y2);
-  action_up.SetToolType(0, MotionEvent::TOOL_TYPE_STYLUS);
+  MockMotionEvent action_up(MotionEvent::Action::UP, event_time, x2, y2);
+  action_up.SetToolType(0, MotionEvent::ToolType::STYLUS);
   action_up.set_button_state(0);
   EXPECT_TRUE(selector_->OnTouchEvent(action_up));
   ASSERT_EQ(1u, event_log_.size());
@@ -282,35 +282,35 @@
   const float y2 = 90.0f;
   const float x3 = 150.0f;
   const float y3 = 150.0f;
-  // 1. ACTION_DOWN with stylus + button
+  // 1. Action::DOWN with stylus + button
   event_time += base::TimeDelta::FromMilliseconds(10);
-  MockMotionEvent action_down(MotionEvent::ACTION_DOWN, event_time, x1, y1);
-  action_down.SetToolType(0, MotionEvent::TOOL_TYPE_STYLUS);
+  MockMotionEvent action_down(MotionEvent::Action::DOWN, event_time, x1, y1);
+  action_down.SetToolType(0, MotionEvent::ToolType::STYLUS);
   action_down.set_button_state(MotionEvent::BUTTON_STYLUS_PRIMARY);
   EXPECT_TRUE(selector_->OnTouchEvent(action_down));
   EXPECT_TRUE(event_log_.empty());
 
-  // 2. ACTION_MOVE
+  // 2. Action::MOVE
   event_time += base::TimeDelta::FromMilliseconds(10);
-  MockMotionEvent action_move(MotionEvent::ACTION_MOVE, event_time, x2, y2);
-  action_move.SetToolType(0, MotionEvent::TOOL_TYPE_STYLUS);
+  MockMotionEvent action_move(MotionEvent::Action::MOVE, event_time, x2, y2);
+  action_move.SetToolType(0, MotionEvent::ToolType::STYLUS);
   action_move.set_button_state(MotionEvent::BUTTON_STYLUS_PRIMARY);
   EXPECT_TRUE(selector_->OnTouchEvent(action_move));
   ASSERT_EQ(1u, event_log_.size());
   EXPECT_STREQ("Begin(50, 30, 100, 90)", event_log_.back().c_str());
 
   event_time += base::TimeDelta::FromMilliseconds(10);
-  action_move = MockMotionEvent(MotionEvent::ACTION_MOVE, event_time, x3, y3);
-  action_move.SetToolType(0, MotionEvent::TOOL_TYPE_STYLUS);
+  action_move = MockMotionEvent(MotionEvent::Action::MOVE, event_time, x3, y3);
+  action_move.SetToolType(0, MotionEvent::ToolType::STYLUS);
   action_move.set_button_state(MotionEvent::BUTTON_STYLUS_PRIMARY);
   EXPECT_TRUE(selector_->OnTouchEvent(action_move));
   ASSERT_EQ(2u, event_log_.size());
   EXPECT_STREQ("Update(150, 150)", event_log_.back().c_str());
 
-  // 3. ACTION_UP
+  // 3. Action::UP
   event_time += base::TimeDelta::FromMilliseconds(10);
-  MockMotionEvent action_up(MotionEvent::ACTION_UP, event_time, x3, y3);
-  action_up.SetToolType(0, MotionEvent::TOOL_TYPE_STYLUS);
+  MockMotionEvent action_up(MotionEvent::Action::UP, event_time, x3, y3);
+  action_up.SetToolType(0, MotionEvent::ToolType::STYLUS);
   action_up.set_button_state(0);
   EXPECT_TRUE(selector_->OnTouchEvent(action_up));
   ASSERT_EQ(3u, event_log_.size());  // NO CHANGE
@@ -322,60 +322,60 @@
   float x = 50.0f;
   float y = 30.0f;
 
-  // 1. ACTION_DOWN with stylus + button
+  // 1. Action::DOWN with stylus + button
   event_time += base::TimeDelta::FromMilliseconds(10);
-  MockMotionEvent action_down(MotionEvent::ACTION_DOWN, event_time, x, y);
-  action_down.SetToolType(0, MotionEvent::TOOL_TYPE_STYLUS);
+  MockMotionEvent action_down(MotionEvent::Action::DOWN, event_time, x, y);
+  action_down.SetToolType(0, MotionEvent::ToolType::STYLUS);
   action_down.set_button_state(MotionEvent::BUTTON_STYLUS_PRIMARY);
   EXPECT_TRUE(selector_->OnTouchEvent(action_down));
   EXPECT_TRUE(event_log_.empty());
 
-  // 2. ACTION_MOVE
+  // 2. Action::MOVE
   event_time += base::TimeDelta::FromMilliseconds(10);
   x += 20;  // 70
   y += 20;  // 50
-  MockMotionEvent action_move(MotionEvent::ACTION_MOVE, event_time, x, y);
-  action_move.SetToolType(0, MotionEvent::TOOL_TYPE_STYLUS);
+  MockMotionEvent action_move(MotionEvent::Action::MOVE, event_time, x, y);
+  action_move.SetToolType(0, MotionEvent::ToolType::STYLUS);
   action_move.set_button_state(MotionEvent::BUTTON_STYLUS_PRIMARY);
   EXPECT_TRUE(selector_->OnTouchEvent(action_move));
   ASSERT_EQ(1u, event_log_.size());
   EXPECT_STREQ("Begin(50, 30, 70, 50)", event_log_.back().c_str());
 
-  // 3. ACTION_MOVE with stylus + no button
+  // 3. Action::MOVE with stylus + no button
   event_time += base::TimeDelta::FromMilliseconds(10);
   x += 20;  // 90
   y += 20;  // 70
-  action_move = MockMotionEvent(MotionEvent::ACTION_MOVE, event_time, x, y);
-  action_move.SetToolType(0, MotionEvent::TOOL_TYPE_STYLUS);
+  action_move = MockMotionEvent(MotionEvent::Action::MOVE, event_time, x, y);
+  action_move.SetToolType(0, MotionEvent::ToolType::STYLUS);
   action_move.set_button_state(0);
   EXPECT_TRUE(selector_->OnTouchEvent(action_move));
   EXPECT_EQ(1u, event_log_.size());  // NO CHANGE
 
-  // 4. ACTION_MOVE with stylus + button pressed again
+  // 4. Action::MOVE with stylus + button pressed again
   //    Note that the end action is deferred until the stylus is lifted.
   event_time += base::TimeDelta::FromMilliseconds(10);
   x += 20;  // 110
   y += 20;  // 90
-  action_move = MockMotionEvent(MotionEvent::ACTION_MOVE, event_time, x, y);
-  action_move.SetToolType(0, MotionEvent::TOOL_TYPE_STYLUS);
+  action_move = MockMotionEvent(MotionEvent::Action::MOVE, event_time, x, y);
+  action_move.SetToolType(0, MotionEvent::ToolType::STYLUS);
   action_move.set_button_state(MotionEvent::BUTTON_STYLUS_PRIMARY);
   EXPECT_TRUE(selector_->OnTouchEvent(action_move));
   EXPECT_EQ(2u, event_log_.size());
   EXPECT_STREQ("Begin(90, 70, 110, 90)", event_log_.back().c_str());
 
-  // 5. ACTION_MOVE with stylus + no button
+  // 5. Action::MOVE with stylus + no button
   event_time += base::TimeDelta::FromMilliseconds(10);
   x += 20;  // 130
   y += 20;  // 110
-  action_move = MockMotionEvent(MotionEvent::ACTION_MOVE, event_time, x, y);
-  action_move.SetToolType(0, MotionEvent::TOOL_TYPE_STYLUS);
+  action_move = MockMotionEvent(MotionEvent::Action::MOVE, event_time, x, y);
+  action_move.SetToolType(0, MotionEvent::ToolType::STYLUS);
   EXPECT_TRUE(selector_->OnTouchEvent(action_move));
   EXPECT_EQ(2u, event_log_.size());  // NO CHANGE
 
-  // 5. ACTION_UP
+  // 5. Action::UP
   event_time += base::TimeDelta::FromMilliseconds(10);
-  MockMotionEvent action_up(MotionEvent::ACTION_UP, event_time, x, y);
-  action_up.SetToolType(0, MotionEvent::TOOL_TYPE_STYLUS);
+  MockMotionEvent action_up(MotionEvent::Action::UP, event_time, x, y);
+  action_up.SetToolType(0, MotionEvent::ToolType::STYLUS);
   action_up.set_button_state(0);
   EXPECT_TRUE(selector_->OnTouchEvent(action_up));
   EXPECT_EQ(3u, event_log_.size());
@@ -388,26 +388,26 @@
   const float y1 = 30.0f;
   const float x2 = 51.0f;
   const float y2 = 31.0f;
-  // 1. ACTION_DOWN with stylus + button
+  // 1. Action::DOWN with stylus + button
   event_time += base::TimeDelta::FromMilliseconds(1);
-  MockMotionEvent action_down(MotionEvent::ACTION_DOWN, event_time, x1, y1);
-  action_down.SetToolType(0, MotionEvent::TOOL_TYPE_STYLUS);
+  MockMotionEvent action_down(MotionEvent::Action::DOWN, event_time, x1, y1);
+  action_down.SetToolType(0, MotionEvent::ToolType::STYLUS);
   action_down.set_button_state(MotionEvent::BUTTON_STYLUS_PRIMARY);
   EXPECT_TRUE(selector_->OnTouchEvent(action_down));
   EXPECT_TRUE(event_log_.empty());
 
-  // 2. ACTION_MOVE
+  // 2. Action::MOVE
   event_time += base::TimeDelta::FromMilliseconds(1);
-  MockMotionEvent action_move(MotionEvent::ACTION_MOVE, event_time, x2, y2);
-  action_move.SetToolType(0, MotionEvent::TOOL_TYPE_STYLUS);
+  MockMotionEvent action_move(MotionEvent::Action::MOVE, event_time, x2, y2);
+  action_move.SetToolType(0, MotionEvent::ToolType::STYLUS);
   action_move.set_button_state(MotionEvent::BUTTON_STYLUS_PRIMARY);
   EXPECT_TRUE(selector_->OnTouchEvent(action_move));
   EXPECT_TRUE(event_log_.empty());
 
-  // 3. ACTION_UP
+  // 3. Action::UP
   event_time += base::TimeDelta::FromMilliseconds(1);
-  MockMotionEvent action_up(MotionEvent::ACTION_UP, event_time, x2, y2);
-  action_up.SetToolType(0, MotionEvent::TOOL_TYPE_STYLUS);
+  MockMotionEvent action_up(MotionEvent::Action::UP, event_time, x2, y2);
+  action_up.SetToolType(0, MotionEvent::ToolType::STYLUS);
   action_up.set_button_state(0);
   EXPECT_TRUE(selector_->OnTouchEvent(action_up));
   ASSERT_EQ(1u, event_log_.size());
diff --git a/content/browser/renderer_host/input/synthetic_gesture_target_mac.mm b/content/browser/renderer_host/input/synthetic_gesture_target_mac.mm
index 83a8792d..36c25c4 100644
--- a/content/browser/renderer_host/input/synthetic_gesture_target_mac.mm
+++ b/content/browser/renderer_host/input/synthetic_gesture_target_mac.mm
@@ -92,30 +92,30 @@
 
     switch (event.GetType()) {
       case WebInputEvent::kGesturePinchBegin: {
-        id event = [SyntheticPinchEvent
+        id cocoa_event = [SyntheticPinchEvent
             eventWithMagnification:0.0f
                   locationInWindow:NSMakePoint(gesture_event->x,
                                                gesture_event->y)
                              phase:NSEventPhaseBegan];
-        [cocoa_view_ handleBeginGestureWithEvent:event];
+        [cocoa_view_ handleBeginGestureWithEvent:cocoa_event];
         return;
       }
       case WebInputEvent::kGesturePinchEnd: {
-        id event = [SyntheticPinchEvent
+        id cocoa_event = [SyntheticPinchEvent
             eventWithMagnification:0.0f
                   locationInWindow:NSMakePoint(gesture_event->x,
                                                gesture_event->y)
                              phase:NSEventPhaseEnded];
-        [cocoa_view_ handleEndGestureWithEvent:event];
+        [cocoa_view_ handleEndGestureWithEvent:cocoa_event];
         return;
       }
       case WebInputEvent::kGesturePinchUpdate: {
-        id event = [SyntheticPinchEvent
+        id cocoa_event = [SyntheticPinchEvent
             eventWithMagnification:gesture_event->data.pinch_update.scale - 1.0f
                   locationInWindow:NSMakePoint(gesture_event->x,
                                                gesture_event->y)
                              phase:NSEventPhaseChanged];
-        [cocoa_view_ magnifyWithEvent:event];
+        [cocoa_view_ magnifyWithEvent:cocoa_event];
         return;
       }
       default:
diff --git a/content/browser/renderer_host/input/web_input_event_builders_android_unittest.cc b/content/browser/renderer_host/input/web_input_event_builders_android_unittest.cc
index 1985e35..22b3649 100644
--- a/content/browser/renderer_host/input/web_input_event_builders_android_unittest.cc
+++ b/content/browser/renderer_host/input/web_input_event_builders_android_unittest.cc
@@ -34,8 +34,7 @@
                                             int web_modifier,
                                             int unicode_character) {
   ScopedJavaLocalRef<jobject> keydown_event =
-      ui::events::android::CreateKeyEvent(env, ui::MotionEvent::ACTION_DOWN,
-                                          key_code);
+      ui::events::android::CreateKeyEvent(env, 0, key_code);
 
   WebKeyboardEvent web_event = content::WebKeyboardEventBuilder::Build(
       env, keydown_event, WebKeyboardEvent::kKeyDown, web_modifier, 0, key_code,
@@ -206,7 +205,8 @@
   clock.SetNowTicks(event_time);
 
   ui::MotionEventAndroid::Pointer p0(1, 13.7f, -7.13f, 5.3f, 1.2f, 0.1f, 0.2f,
-                                     ui::MotionEvent::TOOL_TYPE_MOUSE);
+                                     ui::MotionEventAndroid::GetAndroidToolType(
+                                         ui::MotionEvent::ToolType::MOUSE));
   const float raw_offset_x = 11.f;
   const float raw_offset_y = 22.f;
   const float kPixToDip = 0.5f;
diff --git a/content/browser/renderer_host/input/web_input_event_util_unittest.cc b/content/browser/renderer_host/input/web_input_event_util_unittest.cc
index 152a5e00..3552f36 100644
--- a/content/browser/renderer_host/input/web_input_event_util_unittest.cc
+++ b/content/browser/renderer_host/input/web_input_event_util_unittest.cc
@@ -24,10 +24,9 @@
 namespace content {
 
 TEST(WebInputEventUtilTest, MotionEventConversion) {
-
-  const MotionEvent::ToolType tool_types[] = {MotionEvent::TOOL_TYPE_FINGER,
-                                              MotionEvent::TOOL_TYPE_STYLUS,
-                                              MotionEvent::TOOL_TYPE_MOUSE};
+  const MotionEvent::ToolType tool_types[] = {MotionEvent::ToolType::FINGER,
+                                              MotionEvent::ToolType::STYLUS,
+                                              MotionEvent::ToolType::MOUSE};
   ui::PointerProperties pointer(5, 10, 40);
   pointer.id = 15;
   pointer.raw_x = 20;
@@ -39,8 +38,8 @@
   pointer.tilt_y = 70;
   for (MotionEvent::ToolType tool_type : tool_types) {
     pointer.tool_type = tool_type;
-    MotionEventGeneric event(
-        MotionEvent::ACTION_DOWN, base::TimeTicks::Now(), pointer);
+    MotionEventGeneric event(MotionEvent::Action::DOWN, base::TimeTicks::Now(),
+                             pointer);
     event.set_flags(ui::EF_SHIFT_DOWN | ui::EF_ALT_DOWN);
     event.set_unique_event_id(123456U);
 
@@ -58,7 +57,7 @@
     expected_pointer.radius_y = pointer.touch_minor / 2.f;
     expected_pointer.rotation_angle = 0.f;
     expected_pointer.force = pointer.pressure;
-    if (tool_type == MotionEvent::TOOL_TYPE_STYLUS) {
+    if (tool_type == MotionEvent::ToolType::STYLUS) {
       expected_pointer.tilt_x = 60;
       expected_pointer.tilt_y = 70;
     } else {
@@ -78,7 +77,7 @@
 
 TEST(WebInputEventUtilTest, ScrollUpdateConversion) {
   int motion_event_id = 0;
-  MotionEvent::ToolType tool_type = MotionEvent::TOOL_TYPE_UNKNOWN;
+  MotionEvent::ToolType tool_type = MotionEvent::ToolType::UNKNOWN;
   base::TimeTicks timestamp = base::TimeTicks::Now();
   gfx::Vector2dF delta(-5.f, 10.f);
   gfx::PointF pos(1.f, 2.f);
diff --git a/content/browser/renderer_host/render_message_filter.cc b/content/browser/renderer_host/render_message_filter.cc
index 8abc030..0c8e62e1 100644
--- a/content/browser/renderer_host/render_message_filter.cc
+++ b/content/browser/renderer_host/render_message_filter.cc
@@ -67,7 +67,7 @@
 #include "url/origin.h"
 
 #if defined(OS_WIN)
-#include "content/common/font_cache_dispatcher_win.h"
+#include "content/public/common/font_cache_dispatcher_win.h"
 #endif
 
 #if defined(OS_POSIX)
diff --git a/content/browser/renderer_host/render_process_host_impl.cc b/content/browser/renderer_host/render_process_host_impl.cc
index f304bee..9684ee7 100644
--- a/content/browser/renderer_host/render_process_host_impl.cc
+++ b/content/browser/renderer_host/render_process_host_impl.cc
@@ -99,7 +99,6 @@
 #include "content/browser/media/midi_host.h"
 #include "content/browser/memory/memory_coordinator_impl.h"
 #include "content/browser/mime_registry_impl.h"
-#include "content/browser/mus_util.h"
 #include "content/browser/notifications/notification_message_filter.h"
 #include "content/browser/payments/payment_manager.h"
 #include "content/browser/permissions/permission_service_context.h"
@@ -206,6 +205,7 @@
 #include "third_party/WebKit/public/common/page/launching_process_state.h"
 #include "third_party/WebKit/public/public_features.h"
 #include "third_party/skia/include/core/SkBitmap.h"
+#include "ui/base/ui_base_features.h"
 #include "ui/base/ui_base_switches.h"
 #include "ui/base/ui_base_switches_util.h"
 #include "ui/display/display_switches.h"
@@ -1361,7 +1361,7 @@
 
   InitializeChannelProxy();
 
-  if (!switches::IsMusHostingViz())
+  if (!base::FeatureList::IsEnabled(features::kMash))
     gpu_client_.reset(new GpuClient(GetID()));
 }
 
@@ -2605,7 +2605,6 @@
     switches::kReducedReferrerGranularity,
     switches::kRegisterPepperPlugins,
     switches::kRendererStartupDialog,
-    switches::kRootLayerScrolls,
     switches::kSamplingHeapProfiler,
     switches::kShowPaintRects,
     switches::kStatsCollectionController,
@@ -2687,10 +2686,6 @@
     switches::kIpcDumpDirectory,
     switches::kIpcFuzzerTestcase,
 #endif
-#if BUILDFLAG(ENABLE_MUS)
-    switches::kMus,
-    switches::kMusHostingViz,
-#endif
   };
   renderer_cmd->CopySwitchesFrom(browser_cmd, kSwitchNames,
                                  arraysize(kSwitchNames));
diff --git a/content/browser/renderer_host/render_widget_host_input_event_router.cc b/content/browser/renderer_host/render_widget_host_input_event_router.cc
index 2960fdd..cd657c83 100644
--- a/content/browser/renderer_host/render_widget_host_input_event_router.cc
+++ b/content/browser/renderer_host/render_widget_host_input_event_router.cc
@@ -13,7 +13,6 @@
 #include "components/viz/service/surfaces/surface_manager.h"
 #include "content/browser/compositor/surface_utils.h"
 #include "content/browser/frame_host/render_widget_host_view_guest.h"
-#include "content/browser/mus_util.h"
 #include "content/browser/renderer_host/cursor_manager.h"
 #include "content/browser/renderer_host/render_widget_host_impl.h"
 #include "content/browser/renderer_host/render_widget_host_view_base.h"
diff --git a/content/browser/renderer_host/render_widget_host_view_android.cc b/content/browser/renderer_host/render_widget_host_view_android.cc
index fa17a3ca..c7a1b17a 100644
--- a/content/browser/renderer_host/render_widget_host_view_android.cc
+++ b/content/browser/renderer_host/render_widget_host_view_android.cc
@@ -8,10 +8,8 @@
 
 #include <utility>
 
-#include "base/android/application_status_listener.h"
 #include "base/android/build_info.h"
 #include "base/bind.h"
-#include "base/callback_helpers.h"
 #include "base/command_line.h"
 #include "base/location.h"
 #include "base/logging.h"
@@ -27,12 +25,7 @@
 #include "cc/layers/surface_layer.h"
 #include "cc/trees/latency_info_swap_promise.h"
 #include "cc/trees/layer_tree_host.h"
-#include "components/viz/common/frame_sinks/copy_output_request.h"
-#include "components/viz/common/frame_sinks/copy_output_result.h"
-#include "components/viz/common/gl_helper.h"
-#include "components/viz/common/gpu/context_lost_observer.h"
 #include "components/viz/common/quads/compositor_frame.h"
-#include "components/viz/common/resources/single_release_callback.h"
 #include "components/viz/service/frame_sinks/frame_sink_manager_impl.h"
 #include "components/viz/service/surfaces/surface.h"
 #include "components/viz/service/surfaces/surface_hittest.h"
@@ -47,8 +40,6 @@
 #include "content/browser/android/text_suggestion_host_android.h"
 #include "content/browser/compositor/surface_utils.h"
 #include "content/browser/devtools/render_frame_devtools_agent_host.h"
-#include "content/browser/gpu/browser_gpu_channel_host_factory.h"
-#include "content/browser/gpu/compositor_util.h"
 #include "content/browser/gpu/gpu_process_host.h"
 #include "content/browser/media/android/media_web_contents_observer_android.h"
 #include "content/browser/renderer_host/compositor_impl_android.h"
@@ -64,7 +55,6 @@
 #include "content/browser/renderer_host/render_widget_host_impl.h"
 #include "content/browser/renderer_host/render_widget_host_input_event_router.h"
 #include "content/browser/renderer_host/ui_events_helper.h"
-#include "content/common/gpu_stream_constants.h"
 #include "content/common/input_messages.h"
 #include "content/common/view_messages.h"
 #include "content/public/browser/android/compositor.h"
@@ -76,14 +66,12 @@
 #include "content/public/common/content_features.h"
 #include "content/public/common/content_switches.h"
 #include "content/public/common/use_zoom_for_dsf_policy.h"
-#include "gpu/command_buffer/client/gles2_implementation.h"
-#include "gpu/command_buffer/client/gles2_interface.h"
 #include "ipc/ipc_message_macros.h"
 #include "ipc/ipc_message_start.h"
-#include "skia/ext/image_operations.h"
-#include "third_party/khronos/GLES2/gl2.h"
-#include "third_party/khronos/GLES2/gl2ext.h"
+#include "third_party/skia/include/core/SkBitmap.h"
 #include "third_party/skia/include/core/SkCanvas.h"
+#include "third_party/skia/include/core/SkColor.h"
+#include "third_party/skia/include/core/SkImageInfo.h"
 #include "ui/android/view_android_observer.h"
 #include "ui/android/window_android.h"
 #include "ui/android/window_android_compositor.h"
@@ -96,7 +84,6 @@
 #include "ui/events/blink/did_overscroll_params.h"
 #include "ui/events/blink/web_input_event_traits.h"
 #include "ui/events/gesture_detection/gesture_provider_config_helper.h"
-#include "ui/gfx/android/java_bitmap.h"
 #include "ui/gfx/android/view_configuration.h"
 #include "ui/gfx/geometry/dip_util.h"
 #include "ui/gfx/geometry/size_conversions.h"
@@ -111,228 +98,6 @@
     base::TimeDelta::FromSecondsD(0.5);
 static const float kClickCountRadiusSquaredDIP = 25;
 
-class PendingReadbackLock;
-
-PendingReadbackLock* g_pending_readback_lock = nullptr;
-
-class PendingReadbackLock : public base::RefCounted<PendingReadbackLock> {
- public:
-  PendingReadbackLock() {
-    DCHECK_EQ(g_pending_readback_lock, nullptr);
-    g_pending_readback_lock = this;
-  }
-
- private:
-  friend class base::RefCounted<PendingReadbackLock>;
-
-  ~PendingReadbackLock() {
-    DCHECK_EQ(g_pending_readback_lock, this);
-    g_pending_readback_lock = nullptr;
-  }
-};
-
-using base::android::ApplicationState;
-using base::android::ApplicationStatusListener;
-
-class GLHelperHolder : public viz::ContextLostObserver {
- public:
-  static GLHelperHolder* Create();
-
-  ~GLHelperHolder() override;
-
-  viz::GLHelper* gl_helper() { return gl_helper_.get(); }
-  bool IsLost() {
-    if (!gl_helper_)
-      return true;
-    return provider_->ContextGL()->GetGraphicsResetStatusKHR() != GL_NO_ERROR;
-  }
-
-  void ReleaseIfPossible();
-
- private:
-  GLHelperHolder();
-
-  void Initialize();
-  void OnApplicationStatusChanged(ApplicationState new_state);
-
-  // viz::ContextLostObserver implementation.
-  void OnContextLost() override;
-
-  scoped_refptr<ui::ContextProviderCommandBuffer> provider_;
-  std::unique_ptr<viz::GLHelper> gl_helper_;
-
-  // Set to |false| if there are only stopped activities (or none).
-  bool has_running_or_paused_activities_;
-
-  std::unique_ptr<ApplicationStatusListener> app_status_listener_;
-
-  DISALLOW_COPY_AND_ASSIGN(GLHelperHolder);
-};
-
-GLHelperHolder::GLHelperHolder()
-    : has_running_or_paused_activities_(
-          (ApplicationStatusListener::GetState() ==
-           base::android::APPLICATION_STATE_HAS_RUNNING_ACTIVITIES) ||
-          (ApplicationStatusListener::GetState() ==
-           base::android::APPLICATION_STATE_HAS_PAUSED_ACTIVITIES)) {}
-
-GLHelperHolder::~GLHelperHolder() {
-  if (provider_)
-    provider_->RemoveObserver(this);
-}
-
-GLHelperHolder* GLHelperHolder::Create() {
-  GLHelperHolder* holder = new GLHelperHolder;
-  holder->Initialize();
-  return holder;
-}
-
-void GLHelperHolder::Initialize() {
-  auto* factory = BrowserGpuChannelHostFactory::instance();
-  scoped_refptr<gpu::GpuChannelHost> gpu_channel_host(factory->GetGpuChannel());
-
-  // The Browser Compositor is in charge of reestablishing the channel if its
-  // missing.
-  if (!gpu_channel_host)
-    return;
-
-  int32_t stream_id = kGpuStreamIdDefault;
-  gpu::SchedulingPriority stream_priority = kGpuStreamPriorityUI;
-
-  // This is for an offscreen context, so we don't need the default framebuffer
-  // to have alpha, stencil, depth, antialiasing.
-  gpu::ContextCreationAttribs attributes;
-  attributes.alpha_size = -1;
-  attributes.stencil_size = 0;
-  attributes.depth_size = 0;
-  attributes.samples = 0;
-  attributes.sample_buffers = 0;
-  attributes.bind_generates_resource = false;
-
-  gpu::SharedMemoryLimits limits;
-  // The GLHelper context doesn't do a lot of stuff, so we don't expect it to
-  // need a lot of space for commands.
-  limits.command_buffer_size = 1024;
-  // The transfer buffer is used for shaders among other things, so give some
-  // reasonable but small limit.
-  limits.start_transfer_buffer_size = 4 * 1024;
-  limits.min_transfer_buffer_size = 4 * 1024;
-  limits.max_transfer_buffer_size = 128 * 1024;
-  // Very few allocations from mapped memory pool, so this can be really low.
-  limits.mapped_memory_reclaim_limit = 4 * 1024;
-
-  constexpr bool automatic_flushes = false;
-  constexpr bool support_locking = false;
-  const GURL url("chrome://gpu/RenderWidgetHostViewAndroid");
-
-  provider_ = new ui::ContextProviderCommandBuffer(
-      std::move(gpu_channel_host), factory->GetGpuMemoryBufferManager(),
-      stream_id, stream_priority, gpu::kNullSurfaceHandle, url,
-      automatic_flushes, support_locking, limits, attributes, nullptr,
-      ui::command_buffer_metrics::BROWSER_OFFSCREEN_MAINTHREAD_CONTEXT);
-  auto result = provider_->BindToCurrentThread();
-  if (result != gpu::ContextResult::kSuccess)
-    return;
-  provider_->ContextGL()->TraceBeginCHROMIUM(
-      "gpu_toplevel",
-      base::StringPrintf("CmdBufferImageTransportFactory-%p", provider_.get())
-          .c_str());
-  provider_->AddObserver(this);
-  gl_helper_.reset(
-      new viz::GLHelper(provider_->ContextGL(), provider_->ContextSupport()));
-
-  // Unretained() is safe because |this| owns the following two callbacks.
-  app_status_listener_.reset(new ApplicationStatusListener(base::Bind(
-      &GLHelperHolder::OnApplicationStatusChanged, base::Unretained(this))));
-}
-
-void GLHelperHolder::ReleaseIfPossible() {
-  if (!has_running_or_paused_activities_ && !g_pending_readback_lock) {
-    gl_helper_.reset();
-    if (provider_)
-      provider_->RemoveObserver(this);
-    provider_ = nullptr;
-    // Make sure this will get recreated on next use.
-    DCHECK(IsLost());
-  }
-}
-
-void GLHelperHolder::OnContextLost() {
-  app_status_listener_.reset();
-  gl_helper_.reset();
-  // Need to post a task because the command buffer client cannot be deleted
-  // from within this callback.
-  base::ThreadTaskRunnerHandle::Get()->PostTask(
-      FROM_HERE, base::Bind(&RenderWidgetHostViewAndroid::OnContextLost));
-}
-
-void GLHelperHolder::OnApplicationStatusChanged(ApplicationState new_state) {
-  has_running_or_paused_activities_ =
-      new_state == base::android::APPLICATION_STATE_HAS_RUNNING_ACTIVITIES ||
-      new_state == base::android::APPLICATION_STATE_HAS_PAUSED_ACTIVITIES;
-  ReleaseIfPossible();
-}
-
-GLHelperHolder* GetPostReadbackGLHelperHolder(bool create_if_necessary) {
-  static GLHelperHolder* g_readback_helper_holder = nullptr;
-
-  if (!create_if_necessary && !g_readback_helper_holder)
-    return nullptr;
-
-  if (g_readback_helper_holder && g_readback_helper_holder->IsLost()) {
-    delete g_readback_helper_holder;
-    g_readback_helper_holder = nullptr;
-  }
-
-  if (!g_readback_helper_holder)
-    g_readback_helper_holder = GLHelperHolder::Create();
-
-  return g_readback_helper_holder;
-}
-
-viz::GLHelper* GetPostReadbackGLHelper() {
-  bool create_if_necessary = true;
-  return GetPostReadbackGLHelperHolder(create_if_necessary)->gl_helper();
-}
-
-void ReleaseGLHelper() {
-  bool create_if_necessary = false;
-  GLHelperHolder* holder = GetPostReadbackGLHelperHolder(create_if_necessary);
-
-  if (holder) {
-    holder->ReleaseIfPossible();
-  }
-}
-
-void CopyFromCompositingSurfaceFinished(
-    const ReadbackRequestCallback& callback,
-    std::unique_ptr<viz::SingleReleaseCallback> release_callback,
-    std::unique_ptr<SkBitmap> bitmap,
-    const base::TimeTicks& start_time,
-    scoped_refptr<PendingReadbackLock> readback_lock,
-    bool result) {
-  TRACE_EVENT0(
-      "cc", "RenderWidgetHostViewAndroid::CopyFromCompositingSurfaceFinished");
-  gpu::SyncToken sync_token;
-  if (result) {
-    viz::GLHelper* gl_helper = GetPostReadbackGLHelper();
-    if (gl_helper)
-      gl_helper->GenerateSyncToken(&sync_token);
-  }
-
-  // PostTask() to make sure the |readback_lock| is released. Also do this
-  // synchronous GPU operation in a clean callstack.
-  BrowserThread::PostTask(BrowserThread::UI, FROM_HERE,
-                          base::Bind(&ReleaseGLHelper));
-
-  const bool lost_resource = !sync_token.HasData();
-  release_callback->Run(sync_token, lost_resource);
-  UMA_HISTOGRAM_TIMES(kAsyncReadBackString,
-                      base::TimeTicks::Now() - start_time);
-  ReadbackResponse response = result ? READBACK_SUCCESS : READBACK_FAILED;
-  callback.Run(*bitmap, response);
-}
-
 std::unique_ptr<ui::TouchSelectionController> CreateSelectionController(
     ui::TouchSelectionControllerClient* client,
     ContentViewCore* content_view_core) {
@@ -361,72 +126,15 @@
   return rect;
 }
 
-// TODO(wjmaclean): There is significant overlap between
-// PrepareTextureCopyOutputResult and CopyFromCompositingSurfaceFinished in
-// this file, and the versions in surface_utils.cc. They should
-// be merged. See https://crbug.com/582955
-void PrepareTextureCopyOutputResult(
-    const gfx::Size& dst_size_in_pixel,
-    SkColorType color_type,
-    const base::TimeTicks& start_time,
-    const ReadbackRequestCallback& callback,
-    scoped_refptr<PendingReadbackLock> readback_lock,
-    std::unique_ptr<viz::CopyOutputResult> result) {
-  base::ScopedClosureRunner scoped_callback_runner(
-      base::Bind(callback, SkBitmap(), READBACK_FAILED));
-  TRACE_EVENT0("cc",
-               "RenderWidgetHostViewAndroid::PrepareTextureCopyOutputResult");
-  if (result->IsEmpty())
-    return;
-  DCHECK_EQ(result->format(), viz::CopyOutputResult::Format::RGBA_TEXTURE);
-
-  gpu::Mailbox mailbox = result->GetTextureResult()->mailbox;
-  gpu::SyncToken sync_token = result->GetTextureResult()->sync_token;
-  std::unique_ptr<viz::SingleReleaseCallback> release_callback =
-      result->TakeTextureOwnership();
-
-  viz::GLHelper* gl_helper = GetPostReadbackGLHelper();
-  if (!gl_helper)
-    return;
-  if (!gl_helper->IsReadbackConfigSupported(color_type))
-    color_type = kRGBA_8888_SkColorType;
-
-  gfx::Size output_size_in_pixel;
-  if (dst_size_in_pixel.IsEmpty())
-    output_size_in_pixel = result->size();
-  else
-    output_size_in_pixel = dst_size_in_pixel;
-
-  std::unique_ptr<SkBitmap> bitmap(new SkBitmap);
-  if (!bitmap->tryAllocPixels(SkImageInfo::Make(
-          output_size_in_pixel.width(), output_size_in_pixel.height(),
-          color_type, kPremul_SkAlphaType))) {
-    scoped_callback_runner.ReplaceClosure(
-        base::Bind(callback, SkBitmap(), READBACK_BITMAP_ALLOCATION_FAILURE));
-    return;
-  }
-
-  uint8_t* pixels = static_cast<uint8_t*>(bitmap->getPixels());
-
-  ignore_result(scoped_callback_runner.Release());
-
-  gl_helper->CropScaleReadbackAndCleanMailbox(
-      mailbox, sync_token, result->size(), output_size_in_pixel, pixels,
-      color_type,
-      base::Bind(&CopyFromCompositingSurfaceFinished, callback,
-                 base::Passed(&release_callback), base::Passed(&bitmap),
-                 start_time, readback_lock),
-      viz::GLHelper::SCALER_QUALITY_GOOD);
-}
-
 void RecordToolTypeForActionDown(const ui::MotionEventAndroid& event) {
   ui::MotionEventAndroid::Action action = event.GetAction();
-  if (action == ui::MotionEventAndroid::ACTION_DOWN ||
-      action == ui::MotionEventAndroid::ACTION_POINTER_DOWN ||
-      action == ui::MotionEventAndroid::ACTION_BUTTON_PRESS) {
-    UMA_HISTOGRAM_ENUMERATION("Event.AndroidActionDown.ToolType",
-                              event.GetToolType(0),
-                              ui::MotionEventAndroid::TOOL_TYPE_LAST + 1);
+  if (action == ui::MotionEventAndroid::Action::DOWN ||
+      action == ui::MotionEventAndroid::Action::POINTER_DOWN ||
+      action == ui::MotionEventAndroid::Action::BUTTON_PRESS) {
+    UMA_HISTOGRAM_ENUMERATION(
+        "Event.AndroidActionDown.ToolType",
+        static_cast<int>(event.GetToolType(0)),
+        static_cast<int>(ui::MotionEventAndroid::ToolType::LAST) + 1);
   }
 }
 
@@ -644,7 +352,9 @@
 }
 
 bool RenderWidgetHostViewAndroid::IsSurfaceAvailableForCopy() const {
-  return !using_browser_compositor_ || HasValidFrame();
+  return !using_browser_compositor_ ||
+         (delegated_frame_host_ &&
+          delegated_frame_host_->CanCopyFromCompositingSurface());
 }
 
 void RenderWidgetHostViewAndroid::Show() {
@@ -849,6 +559,12 @@
   wants_animate_only_begin_frames_ = true;
 }
 
+viz::FrameSinkId RenderWidgetHostViewAndroid::GetRootFrameSinkId() {
+  if (view_.GetWindowAndroid() && view_.GetWindowAndroid()->GetCompositor())
+    return view_.GetWindowAndroid()->GetCompositor()->GetFrameSinkId();
+  return viz::FrameSinkId();
+}
+
 viz::SurfaceId RenderWidgetHostViewAndroid::GetCurrentSurfaceId() const {
   return delegated_frame_host_ ? delegated_frame_host_->SurfaceId()
                                : viz::SurfaceId();
@@ -980,7 +696,7 @@
 
   // Send a proactive BeginFrame for this vsync to reduce scroll latency for
   // scroll-inducing touch events. Note that Android's Choreographer ensures
-  // that BeginFrame requests made during ACTION_MOVE dispatch will be honored
+  // that BeginFrame requests made during Action::MOVE dispatch will be honored
   // in the same vsync phase.
   if (observing_root_window_ && result.moved_beyond_slop_region)
     AddBeginFrameRequest(BEGIN_FRAME);
@@ -1091,51 +807,42 @@
 
 void RenderWidgetHostViewAndroid::CopyFromSurface(
     const gfx::Rect& src_subrect,
-    const gfx::Size& dst_size,
+    const gfx::Size& output_size,
     const ReadbackRequestCallback& callback,
     const SkColorType preferred_color_type) {
+  // TODO(crbug/759310): |preferred_color_type| will be removed from this API
+  // soon.
+  DCHECK_EQ(preferred_color_type, kN32_SkColorType);
+
   TRACE_EVENT0("cc", "RenderWidgetHostViewAndroid::CopyFromSurface");
-  if (!host_ || !IsSurfaceAvailableForCopy()) {
+  if (!IsSurfaceAvailableForCopy()) {
     callback.Run(SkBitmap(), READBACK_SURFACE_UNAVAILABLE);
     return;
   }
-  if (!(view_.GetWindowAndroid())) {
-    callback.Run(SkBitmap(), READBACK_FAILED);
-    return;
-  }
 
   base::TimeTicks start_time = base::TimeTicks::Now();
-  float device_scale_factor = view_.GetDipScale();
-  gfx::Size dst_size_in_pixel =
-      gfx::ConvertRectToPixel(device_scale_factor, gfx::Rect(dst_size)).size();
-  // Note: When |src_subrect| is empty, a conversion from the view size must be
-  // made instead of using |current_frame_size_|. The latter sometimes also
-  // includes extra height for the toolbar UI, which is not intended for
-  // capture.
-  gfx::Rect src_subrect_in_pixel = gfx::ConvertRectToPixel(
-      device_scale_factor, src_subrect.IsEmpty()
-                               ? gfx::Rect(GetVisibleViewportSize())
-                               : src_subrect);
 
   if (!using_browser_compositor_) {
-    SynchronousCopyContents(src_subrect_in_pixel, dst_size_in_pixel, callback,
-                            preferred_color_type);
+    SynchronousCopyContents(src_subrect, output_size, callback);
     UMA_HISTOGRAM_TIMES("Compositing.CopyFromSurfaceTimeSynchronous",
                         base::TimeTicks::Now() - start_time);
     return;
   }
 
-  ui::WindowAndroidCompositor* compositor =
-      view_.GetWindowAndroid()->GetCompositor();
-  DCHECK(compositor);
   DCHECK(delegated_frame_host_);
-  scoped_refptr<PendingReadbackLock> readback_lock(
-      g_pending_readback_lock ? g_pending_readback_lock
-                              : new PendingReadbackLock);
-  delegated_frame_host_->RequestCopyOfSurface(
-      compositor, src_subrect_in_pixel,
-      base::Bind(&PrepareTextureCopyOutputResult, dst_size_in_pixel,
-                 preferred_color_type, start_time, callback, readback_lock));
+  delegated_frame_host_->CopyFromCompositingSurface(
+      src_subrect, output_size,
+      base::BindOnce(
+          [](const ReadbackRequestCallback& callback,
+             base::TimeTicks start_time, const SkBitmap& bitmap) {
+            TRACE_EVENT0(
+                "cc", "RenderWidgetHostViewAndroid::CopyFromSurface finished");
+            UMA_HISTOGRAM_TIMES(kAsyncReadBackString,
+                                base::TimeTicks::Now() - start_time);
+            callback.Run(bitmap, bitmap.drawsNothing() ? READBACK_FAILED
+                                                       : READBACK_SUCCESS);
+          },
+          callback, start_time));
 }
 
 void RenderWidgetHostViewAndroid::ShowDisambiguationPopup(
@@ -1420,10 +1127,18 @@
 void RenderWidgetHostViewAndroid::DidScroll() {}
 
 void RenderWidgetHostViewAndroid::SynchronousCopyContents(
-    const gfx::Rect& src_subrect_in_pixel,
+    const gfx::Rect& src_subrect_dip,
     const gfx::Size& dst_size_in_pixel,
-    const ReadbackRequestCallback& callback,
-    const SkColorType color_type) {
+    const ReadbackRequestCallback& callback) {
+  // Note: When |src_subrect| is empty, a conversion from the view size must
+  // be made instead of using |current_frame_size_|. The latter sometimes also
+  // includes extra height for the toolbar UI, which is not intended for
+  // capture.
+  const gfx::Rect src_subrect_in_pixel = gfx::ConvertRectToPixel(
+      view_.GetDipScale(), src_subrect_dip.IsEmpty()
+                               ? gfx::Rect(GetVisibleViewportSize())
+                               : src_subrect_dip);
+
   // TODO(crbug/698974): [BUG] Current implementation does not support read-back
   // of regions that do not originate at (0,0).
   const gfx::Size& input_size_in_pixel = src_subrect_in_pixel.size();
@@ -1443,10 +1158,7 @@
   }
 
   SkBitmap bitmap;
-  bitmap.allocPixels(SkImageInfo::Make(output_width,
-                                       output_height,
-                                       color_type,
-                                       kPremul_SkAlphaType));
+  bitmap.allocPixels(SkImageInfo::MakeN32Premul(output_width, output_height));
   SkCanvas canvas(bitmap);
   canvas.scale(
       (float)output_width / (float)input_size_in_pixel.width(),
@@ -2377,17 +2089,17 @@
   base::TimeTicks event_time = event.GetEventTime();
   base::TimeDelta delta = base::TimeTicks::Now() - event_time;
   switch (event.GetAction()) {
-    case ui::MotionEvent::ACTION_DOWN:
-    case ui::MotionEvent::ACTION_POINTER_DOWN:
+    case ui::MotionEvent::Action::DOWN:
+    case ui::MotionEvent::Action::POINTER_DOWN:
       UMA_HISTOGRAM_CUSTOM_COUNTS("Event.Latency.OS.TOUCH_PRESSED",
                                   delta.InMicroseconds(), 1, 1000000, 50);
       return;
-    case ui::MotionEvent::ACTION_MOVE:
+    case ui::MotionEvent::Action::MOVE:
       UMA_HISTOGRAM_CUSTOM_COUNTS("Event.Latency.OS.TOUCH_MOVED",
                                   delta.InMicroseconds(), 1, 1000000, 50);
       return;
-    case ui::MotionEvent::ACTION_UP:
-    case ui::MotionEvent::ACTION_POINTER_UP:
+    case ui::MotionEvent::Action::UP:
+    case ui::MotionEvent::Action::POINTER_UP:
       UMA_HISTOGRAM_CUSTOM_COUNTS("Event.Latency.OS.TOUCH_RELEASED",
                                   delta.InMicroseconds(), 1, 1000000, 50);
       return;
diff --git a/content/browser/renderer_host/render_widget_host_view_android.h b/content/browser/renderer_host/render_widget_host_view_android.h
index f77fe9bd1..ebef72b 100644
--- a/content/browser/renderer_host/render_widget_host_view_android.h
+++ b/content/browser/renderer_host/render_widget_host_view_android.h
@@ -175,6 +175,7 @@
   bool TransformPointToLocalCoordSpace(const gfx::PointF& point,
                                        const viz::SurfaceId& original_surface,
                                        gfx::PointF* transformed_point) override;
+  viz::FrameSinkId GetRootFrameSinkId() override;
   viz::SurfaceId GetCurrentSurfaceId() const override;
   bool TransformPointToCoordSpaceForView(
       const gfx::PointF& point,
@@ -346,10 +347,9 @@
   void EvictFrameIfNecessary();
 
   // DevTools ScreenCast support for Android WebView.
-  void SynchronousCopyContents(const gfx::Rect& src_subrect_in_pixel,
+  void SynchronousCopyContents(const gfx::Rect& src_subrect_dip,
                                const gfx::Size& dst_size_in_pixel,
-                               const ReadbackRequestCallback& callback,
-                               const SkColorType color_type);
+                               const ReadbackRequestCallback& callback);
 
   void DestroyDelegatedContent();
   void OnLostResources();
diff --git a/content/browser/renderer_host/render_widget_host_view_aura.cc b/content/browser/renderer_host/render_widget_host_view_aura.cc
index 4e27457..a812888 100644
--- a/content/browser/renderer_host/render_widget_host_view_aura.cc
+++ b/content/browser/renderer_host/render_widget_host_view_aura.cc
@@ -32,7 +32,6 @@
 #include "content/browser/frame_host/frame_tree_node.h"
 #include "content/browser/frame_host/render_frame_host_impl.h"
 #include "content/browser/gpu/compositor_util.h"
-#include "content/browser/mus_util.h"
 #include "content/browser/renderer_host/cursor_manager.h"
 #include "content/browser/renderer_host/delegated_frame_host_client_aura.h"
 #include "content/browser/renderer_host/dip_util.h"
@@ -79,6 +78,7 @@
 #include "ui/base/clipboard/scoped_clipboard_writer.h"
 #include "ui/base/hit_test.h"
 #include "ui/base/ime/input_method.h"
+#include "ui/base/ui_base_features.h"
 #include "ui/base/ui_base_switches.h"
 #include "ui/base/ui_base_switches_util.h"
 #include "ui/base/ui_base_types.h"
@@ -411,7 +411,7 @@
       is_guest_view_hack_(is_guest_view_hack),
       device_scale_factor_(0.0f),
       event_handler_(new RenderWidgetHostViewEventHandler(host_, this, this)),
-      frame_sink_id_(switches::IsMusHostingViz()
+      frame_sink_id_(base::FeatureList::IsEnabled(features::kMash)
                          ? viz::FrameSinkId()
                          : is_guest_view_hack_
                                ? AllocateFrameSinkIdForGuestViewHack()
@@ -1731,7 +1731,7 @@
 void RenderWidgetHostViewAura::ScheduleEmbed(
     ui::mojom::WindowTreeClientPtr client,
     base::OnceCallback<void(const base::UnguessableToken&)> callback) {
-  DCHECK(IsUsingMus());
+  DCHECK(features::IsMusEnabled());
   aura::Env::GetInstance()->ScheduleEmbed(std::move(client),
                                           std::move(callback));
 }
@@ -1930,7 +1930,7 @@
   window_->Init(ui::LAYER_SOLID_COLOR);
   window_->layer()->SetColor(background_color_);
 
-  if (!IsUsingMus())
+  if (!features::IsMusEnabled())
     return;
 
   // Embed the renderer into the Window.
diff --git a/content/browser/renderer_host/render_widget_host_view_aura_unittest.cc b/content/browser/renderer_host/render_widget_host_view_aura_unittest.cc
index bf53b082..9c3e62a 100644
--- a/content/browser/renderer_host/render_widget_host_view_aura_unittest.cc
+++ b/content/browser/renderer_host/render_widget_host_view_aura_unittest.cc
@@ -92,6 +92,7 @@
 #include "ui/aura/window_observer.h"
 #include "ui/base/clipboard/clipboard.h"
 #include "ui/base/ime/input_method.h"
+#include "ui/base/ui_base_features.h"
 #include "ui/base/ui_base_switches.h"
 #include "ui/base/ui_base_types.h"
 #include "ui/compositor/compositor.h"
@@ -1613,14 +1614,14 @@
       GetAndResetDispatchedMessages();
   EXPECT_EQ(0U, events.size());
   EXPECT_TRUE(press.synchronous_handling_disabled());
-  EXPECT_EQ(ui::MotionEvent::ACTION_DOWN, pointer_state().GetAction());
+  EXPECT_EQ(ui::MotionEvent::Action::DOWN, pointer_state().GetAction());
 
   view_->OnTouchEvent(&move);
   base::RunLoop().RunUntilIdle();
   events = GetAndResetDispatchedMessages();
   EXPECT_EQ(0U, events.size());
   EXPECT_TRUE(press.synchronous_handling_disabled());
-  EXPECT_EQ(ui::MotionEvent::ACTION_MOVE, pointer_state().GetAction());
+  EXPECT_EQ(ui::MotionEvent::Action::MOVE, pointer_state().GetAction());
   EXPECT_EQ(1U, pointer_state().GetPointerCount());
 
   view_->OnTouchEvent(&release);
@@ -1640,13 +1641,13 @@
   events = GetAndResetDispatchedMessages();
   EXPECT_EQ(1U, events.size());
   EXPECT_TRUE(press.synchronous_handling_disabled());
-  EXPECT_EQ(ui::MotionEvent::ACTION_DOWN, pointer_state().GetAction());
+  EXPECT_EQ(ui::MotionEvent::Action::DOWN, pointer_state().GetAction());
   EXPECT_EQ(1U, pointer_state().GetPointerCount());
 
   view_->OnTouchEvent(&move);
   base::RunLoop().RunUntilIdle();
   EXPECT_TRUE(move.synchronous_handling_disabled());
-  EXPECT_EQ(ui::MotionEvent::ACTION_MOVE, pointer_state().GetAction());
+  EXPECT_EQ(ui::MotionEvent::Action::MOVE, pointer_state().GetAction());
   EXPECT_EQ(1U, pointer_state().GetPointerCount());
   view_->OnTouchEvent(&release);
   EXPECT_TRUE(release.synchronous_handling_disabled());
@@ -1656,7 +1657,7 @@
   view_->OnTouchEvent(&press);
   base::RunLoop().RunUntilIdle();
   EXPECT_TRUE(press.synchronous_handling_disabled());
-  EXPECT_EQ(ui::MotionEvent::ACTION_DOWN, pointer_state().GetAction());
+  EXPECT_EQ(ui::MotionEvent::Action::DOWN, pointer_state().GetAction());
   EXPECT_EQ(1U, pointer_state().GetPointerCount());
   events = GetAndResetDispatchedMessages();
   EXPECT_EQ(3U, events.size());
@@ -1678,7 +1679,7 @@
   view_->OnTouchEvent(&move2);
   base::RunLoop().RunUntilIdle();
   EXPECT_TRUE(press.synchronous_handling_disabled());
-  EXPECT_EQ(ui::MotionEvent::ACTION_MOVE, pointer_state().GetAction());
+  EXPECT_EQ(ui::MotionEvent::Action::MOVE, pointer_state().GetAction());
   EXPECT_EQ(1U, pointer_state().GetPointerCount());
 
   ui::TouchEvent release2(
@@ -2081,7 +2082,7 @@
       GetAndResetDispatchedMessages();
   EXPECT_EQ("SetFocus TouchStart", GetMessageNames(events));
   events[1]->ToEvent()->CallCallback(INPUT_EVENT_ACK_STATE_CONSUMED);
-  EXPECT_EQ(ui::MotionEvent::ACTION_DOWN, pointer_state().GetAction());
+  EXPECT_EQ(ui::MotionEvent::Action::DOWN, pointer_state().GetAction());
   EXPECT_EQ(1U, pointer_state().GetPointerCount());
   EXPECT_EQ(1U, view_->dispatcher_->GetAndResetProcessedTouchEventCount());
 
@@ -2094,7 +2095,7 @@
   events = GetAndResetDispatchedMessages();
   EXPECT_EQ("TouchMove", GetMessageNames(events));
   events[0]->ToEvent()->CallCallback(INPUT_EVENT_ACK_STATE_CONSUMED);
-  EXPECT_EQ(ui::MotionEvent::ACTION_MOVE, pointer_state().GetAction());
+  EXPECT_EQ(ui::MotionEvent::Action::MOVE, pointer_state().GetAction());
   EXPECT_EQ(1U, pointer_state().GetPointerCount());
   EXPECT_EQ(1U, view_->dispatcher_->GetAndResetProcessedTouchEventCount());
 
@@ -2109,7 +2110,7 @@
   events = GetAndResetDispatchedMessages();
   EXPECT_EQ("TouchStart", GetMessageNames(events));
   events[0]->ToEvent()->CallCallback(INPUT_EVENT_ACK_STATE_CONSUMED);
-  EXPECT_EQ(ui::MotionEvent::ACTION_POINTER_DOWN, pointer_state().GetAction());
+  EXPECT_EQ(ui::MotionEvent::Action::POINTER_DOWN, pointer_state().GetAction());
   EXPECT_EQ(1, pointer_state().GetActionIndex());
   EXPECT_EQ(2U, pointer_state().GetPointerCount());
   EXPECT_EQ(1U, view_->dispatcher_->GetAndResetProcessedTouchEventCount());
@@ -2125,7 +2126,7 @@
   events = GetAndResetDispatchedMessages();
   EXPECT_EQ("TouchMove", GetMessageNames(events));
   events[0]->ToEvent()->CallCallback(INPUT_EVENT_ACK_STATE_CONSUMED);
-  EXPECT_EQ(ui::MotionEvent::ACTION_MOVE, pointer_state().GetAction());
+  EXPECT_EQ(ui::MotionEvent::Action::MOVE, pointer_state().GetAction());
   EXPECT_EQ(2U, pointer_state().GetPointerCount());
   EXPECT_EQ(1U, view_->dispatcher_->GetAndResetProcessedTouchEventCount());
 
@@ -2140,7 +2141,7 @@
   events = GetAndResetDispatchedMessages();
   EXPECT_EQ("TouchMove", GetMessageNames(events));
   events[0]->ToEvent()->CallCallback(INPUT_EVENT_ACK_STATE_CONSUMED);
-  EXPECT_EQ(ui::MotionEvent::ACTION_MOVE, pointer_state().GetAction());
+  EXPECT_EQ(ui::MotionEvent::Action::MOVE, pointer_state().GetAction());
   EXPECT_EQ(2U, pointer_state().GetPointerCount());
   EXPECT_EQ(1U, view_->dispatcher_->GetAndResetProcessedTouchEventCount());
 
@@ -2189,19 +2190,19 @@
 
   view_->OnTouchEvent(&press);
   EXPECT_TRUE(press.synchronous_handling_disabled());
-  EXPECT_EQ(ui::MotionEvent::ACTION_DOWN, pointer_state().GetAction());
+  EXPECT_EQ(ui::MotionEvent::Action::DOWN, pointer_state().GetAction());
   EXPECT_EQ(1U, pointer_state().GetPointerCount());
 
   view_->OnTouchEvent(&move);
   EXPECT_TRUE(move.synchronous_handling_disabled());
-  EXPECT_EQ(ui::MotionEvent::ACTION_MOVE, pointer_state().GetAction());
+  EXPECT_EQ(ui::MotionEvent::Action::MOVE, pointer_state().GetAction());
   EXPECT_EQ(1U, pointer_state().GetPointerCount());
 
   // Send the same move event. Since the point hasn't moved, it won't affect the
   // queue. However, the view should consume the event.
   view_->OnTouchEvent(&move);
   EXPECT_TRUE(move.synchronous_handling_disabled());
-  EXPECT_EQ(ui::MotionEvent::ACTION_MOVE, pointer_state().GetAction());
+  EXPECT_EQ(ui::MotionEvent::Action::MOVE, pointer_state().GetAction());
   EXPECT_EQ(1U, pointer_state().GetPointerCount());
 
   view_->OnTouchEvent(&release);
@@ -2529,6 +2530,10 @@
 
 // This test verifies that returned resources do not require a pending ack.
 TEST_F(RenderWidgetHostViewAuraTest, ReturnedResources) {
+  // TODO: fix for mash.
+  if (base::FeatureList::IsEnabled(features::kMash))
+    return;
+
   gfx::Size view_size(100, 100);
   gfx::Rect view_rect(view_size);
 
@@ -2557,6 +2562,10 @@
 // This test verifies that when the CompositorFrameSink changes, the old
 // resources are not returned.
 TEST_F(RenderWidgetHostViewAuraTest, TwoOutputSurfaces) {
+  // TODO: fix for mash.
+  if (base::FeatureList::IsEnabled(features::kMash))
+    return;
+
   viz::FakeSurfaceObserver manager_observer;
   ImageTransportFactory* factory = ImageTransportFactory::GetInstance();
   viz::SurfaceManager* manager = factory->GetContextFactoryPrivate()
@@ -2726,6 +2735,10 @@
 }
 
 TEST_F(RenderWidgetHostViewAuraTest, BackgroundColorMatchesCompositorFrame) {
+  // TODO: fix for mash.
+  if (base::FeatureList::IsEnabled(features::kMash))
+    return;
+
   gfx::Size frame_size(100, 100);
   viz::LocalSurfaceId local_surface_id =
       parent_local_surface_id_allocator_.GenerateId();
@@ -3109,6 +3122,10 @@
 }
 
 TEST_F(RenderWidgetHostViewAuraTest, OutputSurfaceIdChange) {
+  // TODO: fix for mash.
+  if (base::FeatureList::IsEnabled(features::kMash))
+    return;
+
   gfx::Rect view_rect(100, 100);
   gfx::Size frame_size = view_rect.size();
 
@@ -3165,6 +3182,10 @@
 // then the fallback is dropped.
 TEST_F(RenderWidgetHostViewAuraSurfaceSynchronizationTest,
        DropFallbackWhenHidden) {
+  // TODO: fix for mash.
+  if (base::FeatureList::IsEnabled(features::kMash))
+    return;
+
   view_->InitAsChild(nullptr);
   aura::client::ParentWindowWithContext(
       view_->GetNativeView(), parent_view_->GetNativeView()->GetRootWindow(),
@@ -3190,6 +3211,10 @@
 // This test verifies that the primary SurfaceId is populated on resize and
 // the fallback SurfaceId is populated on SubmitCompositorFrame.
 TEST_F(RenderWidgetHostViewAuraSurfaceSynchronizationTest, SurfaceChanges) {
+  // TODO: fix for mash.
+  if (base::FeatureList::IsEnabled(features::kMash))
+    return;
+
   view_->InitAsChild(nullptr);
   aura::client::ParentWindowWithContext(
       view_->GetNativeView(), parent_view_->GetNativeView()->GetRootWindow(),
@@ -3225,6 +3250,10 @@
 // factor changes.
 TEST_F(RenderWidgetHostViewAuraSurfaceSynchronizationTest,
        DeviceScaleFactorChanges) {
+  // TODO: fix for mash.
+  if (base::FeatureList::IsEnabled(features::kMash))
+    return;
+
   view_->InitAsChild(nullptr);
   aura::client::ParentWindowWithContext(
       view_->GetNativeView(), parent_view_->GetNativeView()->GetRootWindow(),
@@ -3252,6 +3281,10 @@
 // the current surface) does not crash,
 TEST_F(RenderWidgetHostViewAuraSurfaceSynchronizationTest,
        CompositorFrameSinkChange) {
+  // TODO: fix for mash.
+  if (base::FeatureList::IsEnabled(features::kMash))
+    return;
+
   gfx::Rect view_rect(100, 100);
   gfx::Size frame_size = view_rect.size();
 
@@ -3282,6 +3315,10 @@
 // RenderWidgetHostViewAuraTest.DiscardDelegatedFrame.
 TEST_F(RenderWidgetHostViewAuraSurfaceSynchronizationTest,
        DiscardDelegatedFrames) {
+  // TODO: fix for mash.
+  if (base::FeatureList::IsEnabled(features::kMash))
+    return;
+
   view_->InitAsChild(nullptr);
 
   size_t max_renderer_frames =
@@ -3571,6 +3608,10 @@
 }
 
 TEST_F(RenderWidgetHostViewAuraTest, DiscardDelegatedFramesWithLocking) {
+  // TODO: fix for mash.
+  if (base::FeatureList::IsEnabled(features::kMash))
+    return;
+
   view_->InitAsChild(nullptr);
 
   size_t max_renderer_frames =
@@ -3640,6 +3681,10 @@
 // Test that changing the memory pressure should delete saved frames. This test
 // only applies to ChromeOS.
 TEST_F(RenderWidgetHostViewAuraTest, DiscardDelegatedFramesWithMemoryPressure) {
+  // TODO: fix for mash.
+  if (base::FeatureList::IsEnabled(features::kMash))
+    return;
+
   view_->InitAsChild(nullptr);
 
   // The test logic below relies on having max_renderer_frames > 2.  By default,
@@ -3740,6 +3785,10 @@
 // SwapCompositorFrame and OnDidNotProduceFrame IPCs through DelegatedFrameHost
 // and its CompositorFrameSinkSupport.
 TEST_F(RenderWidgetHostViewAuraTest, ForwardsBeginFrameAcks) {
+  // TODO: fix for mash.
+  if (base::FeatureList::IsEnabled(features::kMash))
+    return;
+
   gfx::Rect view_rect(100, 100);
   gfx::Size frame_size = view_rect.size();
   viz::LocalSurfaceId local_surface_id = kArbitraryLocalSurfaceId;
@@ -3825,7 +3874,7 @@
   press.set_root_location_f(gfx::PointF(kX, kY));
 
   view_->OnTouchEvent(&press);
-  EXPECT_EQ(ui::MotionEvent::ACTION_DOWN, pointer_state().GetAction());
+  EXPECT_EQ(ui::MotionEvent::Action::DOWN, pointer_state().GetAction());
   EXPECT_EQ(1U, pointer_state().GetPointerCount());
   EXPECT_EQ(kX, pointer_state().GetX(0));
   EXPECT_EQ(kY, pointer_state().GetY(0));
@@ -5829,6 +5878,10 @@
 // SubmitCompositorFrame becomes the active hit test region in the
 // viz::HitTestManager.
 TEST_F(RenderWidgetHostViewAuraTest, HitTestRegionListSubmitted) {
+  // TODO: fix for mash.
+  if (base::FeatureList::IsEnabled(features::kMash))
+    return;
+
   gfx::Rect view_rect(0, 0, 100, 100);
   gfx::Size frame_size = view_rect.size();
 
@@ -5861,6 +5914,10 @@
 // time passes without receiving a new compositor frame.
 TEST_F(RenderWidgetHostViewAuraSurfaceSynchronizationTest,
        NewContentRenderingTimeout) {
+  // TODO: fix for mash.
+  if (base::FeatureList::IsEnabled(features::kMash))
+    return;
+
   view_->InitAsChild(nullptr);
   aura::client::ParentWindowWithContext(
       view_->GetNativeView(), parent_view_->GetNativeView()->GetRootWindow(),
@@ -5914,6 +5971,10 @@
 // If a tab is evicted, allocate a new LocalSurfaceId next time it's shown.
 TEST_F(RenderWidgetHostViewAuraSurfaceSynchronizationTest,
        AllocateLocalSurfaceIdOnEviction) {
+  // TODO: fix for mash.
+  if (base::FeatureList::IsEnabled(features::kMash))
+    return;
+
   view_->InitAsChild(nullptr);
   aura::client::ParentWindowWithContext(
       view_->GetNativeView(), parent_view_->GetNativeView()->GetRootWindow(),
diff --git a/content/browser/renderer_host/render_widget_host_view_child_frame.cc b/content/browser/renderer_host/render_widget_host_view_child_frame.cc
index 269958c..5baab0b 100644
--- a/content/browser/renderer_host/render_widget_host_view_child_frame.cc
+++ b/content/browser/renderer_host/render_widget_host_view_child_frame.cc
@@ -25,7 +25,6 @@
 #include "content/browser/browser_plugin/browser_plugin_guest.h"
 #include "content/browser/compositor/surface_utils.h"
 #include "content/browser/gpu/compositor_util.h"
-#include "content/browser/mus_util.h"
 #include "content/browser/renderer_host/display_util.h"
 #include "content/browser/renderer_host/frame_connector_delegate.h"
 #include "content/browser/renderer_host/input/touch_selection_controller_client_child_frame.h"
@@ -42,7 +41,7 @@
 #include "gpu/ipc/common/gpu_messages.h"
 #include "services/service_manager/runner/common/client_util.h"
 #include "third_party/WebKit/public/platform/WebTouchEvent.h"
-#include "ui/base/ui_base_switches_util.h"
+#include "ui/base/ui_base_features.h"
 #include "ui/gfx/geometry/dip_util.h"
 #include "ui/gfx/geometry/size_conversions.h"
 #include "ui/gfx/geometry/size_f.h"
@@ -77,7 +76,7 @@
       background_color_(SK_ColorWHITE),
       scroll_bubbling_state_(NO_ACTIVE_GESTURE_SCROLL),
       weak_factory_(this) {
-  if (switches::IsMusHostingViz()) {
+  if (base::FeatureList::IsEnabled(features::kMash)) {
     // In Mus the RenderFrameProxy will eventually assign a viz::FrameSinkId
     // until then set ours invalid, as operations using it will be disregarded.
     frame_sink_id_ = viz::FrameSinkId();
@@ -96,7 +95,7 @@
   if (frame_connector_)
     DetachFromTouchSelectionClientManagerIfNecessary();
 
-  if (!switches::IsMusHostingViz()) {
+  if (!base::FeatureList::IsEnabled(features::kMash)) {
     ResetCompositorFrameSinkSupport();
     if (GetHostFrameSinkManager())
       GetHostFrameSinkManager()->InvalidateFrameSinkId(frame_sink_id_);
@@ -151,7 +150,7 @@
 
   if (parent_view) {
     DCHECK(parent_view->GetFrameSinkId().is_valid() ||
-           switches::IsMusHostingViz());
+           base::FeatureList::IsEnabled(features::kMash));
     SetParentFrameSinkId(parent_view->GetFrameSinkId());
   }
 
@@ -172,7 +171,7 @@
   }
 
 #if defined(USE_AURA)
-  if (IsUsingMus()) {
+  if (features::IsMusEnabled()) {
     frame_connector_->EmbedRendererWindowTreeClientInParent(
         GetWindowTreeClientFromRenderer());
   }
@@ -182,7 +181,7 @@
 #if defined(USE_AURA)
 void RenderWidgetHostViewChildFrame::SetFrameSinkId(
     const viz::FrameSinkId& frame_sink_id) {
-  if (switches::IsMusHostingViz())
+  if (base::FeatureList::IsEnabled(features::kMash))
     frame_sink_id_ = frame_sink_id;
 }
 #endif  // defined(USE_AURA)
@@ -561,7 +560,7 @@
 void RenderWidgetHostViewChildFrame::SetParentFrameSinkId(
     const viz::FrameSinkId& parent_frame_sink_id) {
   if (parent_frame_sink_id_ == parent_frame_sink_id ||
-      switches::IsMusHostingViz())
+      base::FeatureList::IsEnabled(features::kMash))
     return;
 
   auto* host_frame_sink_manager = GetHostFrameSinkManager();
@@ -607,7 +606,7 @@
 }
 
 void RenderWidgetHostViewChildFrame::SendSurfaceInfoToEmbedder() {
-  if (switches::IsMusHostingViz())
+  if (base::FeatureList::IsEnabled(features::kMash))
     return;
   viz::SurfaceId surface_id(frame_sink_id_, last_received_local_surface_id_);
   viz::SurfaceInfo surface_info(surface_id, current_surface_scale_factor_,
@@ -1039,7 +1038,7 @@
 }
 
 void RenderWidgetHostViewChildFrame::CreateCompositorFrameSinkSupport() {
-  if (switches::IsMusHostingViz() || enable_viz_)
+  if (base::FeatureList::IsEnabled(features::kMash) || enable_viz_)
     return;
 
   DCHECK(!support_);
diff --git a/content/browser/renderer_host/render_widget_host_view_child_frame_browsertest.cc b/content/browser/renderer_host/render_widget_host_view_child_frame_browsertest.cc
index 865e049..fcc9890 100644
--- a/content/browser/renderer_host/render_widget_host_view_child_frame_browsertest.cc
+++ b/content/browser/renderer_host/render_widget_host_view_child_frame_browsertest.cc
@@ -2,12 +2,12 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+#include "content/browser/renderer_host/render_widget_host_view_child_frame.h"
+
 #include "base/macros.h"
 #include "base/run_loop.h"
 #include "components/viz/common/surfaces/surface_id.h"
 #include "content/browser/frame_host/frame_tree_node.h"
-#include "content/browser/mus_util.h"
-#include "content/browser/renderer_host/render_widget_host_view_child_frame.h"
 #include "content/browser/web_contents/web_contents_impl.h"
 #include "content/common/frame_messages.h"
 #include "content/common/view_messages.h"
@@ -22,7 +22,7 @@
 #include "content/test/test_content_browser_client.h"
 #include "net/dns/mock_host_resolver.h"
 #include "net/test/embedded_test_server/embedded_test_server.h"
-#include "ui/base/ui_base_switches_util.h"
+#include "ui/base/ui_base_features.h"
 #include "ui/gfx/geometry/size.h"
 
 namespace content {
@@ -157,7 +157,7 @@
 IN_PROC_BROWSER_TEST_F(RenderWidgetHostViewChildFrameTest, ChildFrameSinkId) {
   // Only when mus hosts viz do we expect a RenderFrameProxy to provide the
   // FrameSinkId.
-  if (!switches::IsMusHostingViz())
+  if (!base::FeatureList::IsEnabled(features::kMash))
     return;
 
   GURL main_url(embedded_test_server()->GetURL("/site_per_process_main.html"));
diff --git a/content/browser/renderer_host/render_widget_host_view_child_frame_unittest.cc b/content/browser/renderer_host/render_widget_host_view_child_frame_unittest.cc
index 06e81b8..c0d565c5 100644
--- a/content/browser/renderer_host/render_widget_host_view_child_frame_unittest.cc
+++ b/content/browser/renderer_host/render_widget_host_view_child_frame_unittest.cc
@@ -38,6 +38,7 @@
 #include "content/test/mock_widget_impl.h"
 #include "content/test/test_render_view_host.h"
 #include "testing/gtest/include/gtest/gtest.h"
+#include "ui/base/ui_base_features.h"
 #include "ui/compositor/compositor.h"
 
 namespace content {
@@ -201,6 +202,10 @@
 // Verify that SubmitCompositorFrame behavior is correct when a delegated
 // frame is received from a renderer process.
 TEST_F(RenderWidgetHostViewChildFrameTest, SwapCompositorFrame) {
+  // TODO: fix for mash.
+  if (base::FeatureList::IsEnabled(features::kMash))
+    return;
+
   gfx::Size view_size(100, 100);
   gfx::Rect view_rect(view_size);
   float scale_factor = 1.f;
@@ -233,6 +238,10 @@
 
 // Check that the same local surface id can be used after frame eviction.
 TEST_F(RenderWidgetHostViewChildFrameTest, FrameEviction) {
+  // TODO: fix for mash.
+  if (base::FeatureList::IsEnabled(features::kMash))
+    return;
+
   gfx::Size view_size(100, 100);
   gfx::Rect view_rect(view_size);
   float scale_factor = 1.f;
diff --git a/content/browser/service_manager/common_browser_interfaces.cc b/content/browser/service_manager/common_browser_interfaces.cc
index caa6252..282e036 100644
--- a/content/browser/service_manager/common_browser_interfaces.cc
+++ b/content/browser/service_manager/common_browser_interfaces.cc
@@ -20,26 +20,17 @@
 #include "content/public/common/service_manager_connection.h"
 #include "mojo/public/cpp/bindings/interface_request.h"
 #include "services/service_manager/public/cpp/binder_registry.h"
-#include "ui/base/ui_base_switches.h"
+#include "ui/base/ui_base_features.h"
 
 #if defined(OS_WIN)
 #include "content/browser/renderer_host/dwrite_font_proxy_message_filter_win.h"
-#include "content/common/font_cache_dispatcher_win.h"
+#include "content/public/common/font_cache_dispatcher_win.h"
 #endif
 
 namespace content {
 
 namespace {
 
-bool IsRunningWithMus() {
-#if BUILDFLAG(ENABLE_MUS)
-  const base::CommandLine* cmdline = base::CommandLine::ForCurrentProcess();
-  return cmdline->HasSwitch(switches::kMus);
-#else
-  return false;
-#endif
-}
-
 class ConnectionFilterImpl : public ConnectionFilter {
  public:
   ConnectionFilterImpl() {
@@ -50,7 +41,7 @@
         base::CreateSequencedTaskRunnerWithTraits(
             {base::TaskPriority::USER_BLOCKING, base::MayBlock()}));
 #endif
-    if (!IsRunningWithMus()) {
+    if (!features::IsMusEnabled()) {
       // For mus, the mojom::discardable_memory::DiscardableSharedMemoryManager
       // is exposed from ui::Service. So we don't need bind the interface here.
       auto* browser_main_loop = BrowserMainLoop::GetInstance();
diff --git a/content/browser/service_manager/service_manager_context.cc b/content/browser/service_manager/service_manager_context.cc
index 663ee9b..cfee5416 100644
--- a/content/browser/service_manager/service_manager_context.cc
+++ b/content/browser/service_manager/service_manager_context.cc
@@ -25,7 +25,6 @@
 #include "content/browser/browser_main_loop.h"
 #include "content/browser/child_process_launcher.h"
 #include "content/browser/gpu/gpu_process_host.h"
-#include "content/browser/mus_util.h"
 #include "content/browser/service_manager/common_browser_interfaces.h"
 #include "content/browser/utility_process_host_impl.h"
 #include "content/browser/wake_lock/wake_lock_context_host.h"
@@ -77,7 +76,7 @@
 #include "services/video_capture/public/mojom/constants.mojom.h"
 #include "services/video_capture/service_impl.h"
 #include "services/viz/public/interfaces/constants.mojom.h"
-#include "ui/base/ui_base_switches_util.h"
+#include "ui/base/ui_base_features.h"
 #include "ui/base/ui_features.h"
 
 #if defined(OS_ANDROID)
@@ -291,7 +290,7 @@
   params.resource_runner = task_runner;
   params.image_cursors_set_weak_ptr = image_cursors_set_weak_ptr;
   params.memory_manager = memory_manager;
-  params.should_host_viz = switches::IsMusHostingViz();
+  params.should_host_viz = base::FeatureList::IsEnabled(features::kMash);
   return std::make_unique<ui::Service>(params);
 }
 
@@ -301,10 +300,10 @@
   if (!BrowserMainLoop::GetInstance())
     return;
   // Do not embed the UI service when running in mash.
-  if (base::CommandLine::ForCurrentProcess()->HasSwitch("mash"))
+  if (base::FeatureList::IsEnabled(features::kMash))
     return;
-  // Do not embed the UI service if not running with --mus.
-  if (!IsUsingMus())
+  // Do not embed the UI service if not running with mus.
+  if (!features::IsMusEnabled())
     return;
 
   service_manager::EmbeddedServiceInfo info;
diff --git a/content/browser/site_per_process_browsertest.cc b/content/browser/site_per_process_browsertest.cc
index a53ffde..81ce399 100644
--- a/content/browser/site_per_process_browsertest.cc
+++ b/content/browser/site_per_process_browsertest.cc
@@ -9364,22 +9364,22 @@
     // Get main frame view for event insertion.
     RenderWidgetHostViewAndroid* main_view = GetRenderWidgetHostViewAndroid();
 
-    SendTouch(main_view, ui::MotionEvent::ACTION_DOWN, point);
+    SendTouch(main_view, ui::MotionEvent::Action::DOWN, point);
     // action_timeout() is far longer than needed for a LongPress, so we use
     // a custom timeout here.
     DelayBy(base::TimeDelta::FromMilliseconds(2000));
-    SendTouch(main_view, ui::MotionEvent::ACTION_UP, point);
+    SendTouch(main_view, ui::MotionEvent::Action::UP, point);
   }
 
   void SimpleTap(gfx::Point point) {
     // Get main frame view for event insertion.
     RenderWidgetHostViewAndroid* main_view = GetRenderWidgetHostViewAndroid();
 
-    SendTouch(main_view, ui::MotionEvent::ACTION_DOWN, point);
+    SendTouch(main_view, ui::MotionEvent::Action::DOWN, point);
     // tiny_timeout() is way shorter than a reasonable user-created tap gesture,
     // so we use a custom timeout here.
     DelayBy(base::TimeDelta::FromMilliseconds(300));
-    SendTouch(main_view, ui::MotionEvent::ACTION_UP, point);
+    SendTouch(main_view, ui::MotionEvent::Action::UP, point);
   }
 
  protected:
@@ -9394,16 +9394,16 @@
   void SendTouch(RenderWidgetHostViewAndroid* view,
                  ui::MotionEvent::Action action,
                  gfx::Point point) {
-    DCHECK(action >= ui::MotionEvent::ACTION_DOWN &&
-           action < ui::MotionEvent::ACTION_CANCEL);
+    DCHECK(action >= ui::MotionEvent::Action::DOWN &&
+           action < ui::MotionEvent::Action::CANCEL);
 
     ui::MotionEventAndroid::Pointer p(0, point.x(), point.y(), 10, 0, 0, 0, 0);
     JNIEnv* env = base::android::AttachCurrentThread();
     auto time_ms = (ui::EventTimeForNow() - base::TimeTicks()).InMilliseconds();
     ui::MotionEventAndroid touch(
         env, nullptr, 1.f, 0, 0, 0, time_ms,
-        ui::MotionEventAndroid::GetAndroidActionForTesting(action), 1, 0, 0, 0,
-        0, 0, 0, 0, false, &p, nullptr);
+        ui::MotionEventAndroid::GetAndroidAction(action), 1, 0, 0, 0, 0, 0, 0,
+        0, false, &p, nullptr);
     view->OnTouchEvent(touch);
   }
 };
diff --git a/content/browser/tracing/tracing_controller_browsertest.cc b/content/browser/tracing/tracing_controller_browsertest.cc
index 0e0fab6a..177231f 100644
--- a/content/browser/tracing/tracing_controller_browsertest.cc
+++ b/content/browser/tracing/tracing_controller_browsertest.cc
@@ -473,4 +473,21 @@
   run_loop.Run();
 }
 
+IN_PROC_BROWSER_TEST_F(TracingControllerTest, DoubleStopTracing) {
+  Navigate(shell());
+
+  base::RunLoop run_loop;
+  TracingController* controller = TracingController::GetInstance();
+  EXPECT_TRUE(controller->StartTracing(
+      TraceConfig(), TracingController::StartTracingDoneCallback()));
+  EXPECT_TRUE(controller->StopTracing(
+      TracingControllerImpl::CreateCallbackEndpoint(base::BindRepeating(
+          [](base::Closure quit_closure,
+             std::unique_ptr<const base::DictionaryValue> metadata,
+             base::RefCountedString* trace_str) { quit_closure.Run(); },
+          run_loop.QuitClosure()))));
+  EXPECT_FALSE(controller->StopTracing(nullptr));
+  run_loop.Run();
+}
+
 }  // namespace content
diff --git a/content/browser/tracing/tracing_controller_impl.cc b/content/browser/tracing/tracing_controller_impl.cc
index af5de8f..6577d7f 100644
--- a/content/browser/tracing/tracing_controller_impl.cc
+++ b/content/browser/tracing/tracing_controller_impl.cc
@@ -323,7 +323,7 @@
 bool TracingControllerImpl::StopTracing(
     const scoped_refptr<TraceDataEndpoint>& trace_data_endpoint,
     const std::string& agent_label) {
-  if (!IsTracing())
+  if (!IsTracing() || drainer_)
     return false;
   DCHECK_CURRENTLY_ON(BrowserThread::UI);
 
@@ -397,6 +397,7 @@
   filtered_metadata_.reset(nullptr);
   trace_data_endpoint_ = nullptr;
   trace_config_ = nullptr;
+  drainer_ = nullptr;
 }
 
 void TracingControllerImpl::OnDataComplete() {
diff --git a/content/browser/utility_process_host_impl.cc b/content/browser/utility_process_host_impl.cc
index 4f683c2..cc96884 100644
--- a/content/browser/utility_process_host_impl.cc
+++ b/content/browser/utility_process_host_impl.cc
@@ -308,10 +308,6 @@
 #if defined(OS_MACOSX)
       switches::kEnableSandboxLogging,
 #endif
-#if defined(USE_AURA)
-      switches::kMus,
-      switches::kMusHostingViz,
-#endif
       switches::kUseFakeDeviceForMediaStream,
       switches::kUseFileForFakeVideoCapture,
       switches::kUseMockCertVerifierForTesting,
diff --git a/content/browser/web_contents/web_contents_android.cc b/content/browser/web_contents/web_contents_android.cc
index f7eaef21..d9c1cca 100644
--- a/content/browser/web_contents/web_contents_android.cc
+++ b/content/browser/web_contents/web_contents_android.cc
@@ -401,19 +401,6 @@
   web_contents_->ExitFullscreen(/*will_cause_resize=*/false);
 }
 
-void WebContentsAndroid::UpdateBrowserControlsState(
-    JNIEnv* env,
-    const JavaParamRef<jobject>& obj,
-    bool enable_hiding,
-    bool enable_showing,
-    bool animate) {
-  RenderViewHost* host = web_contents_->GetRenderViewHost();
-  if (!host)
-    return;
-  host->Send(new ViewMsg_UpdateBrowserControlsState(
-      host->GetRoutingID(), enable_hiding, enable_showing, animate));
-}
-
 void WebContentsAndroid::ScrollFocusedEditableNodeIntoView(
     JNIEnv* env,
     const JavaParamRef<jobject>& obj) {
diff --git a/content/browser/web_contents/web_contents_android.h b/content/browser/web_contents/web_contents_android.h
index 4354ec7..37cdcb4 100644
--- a/content/browser/web_contents/web_contents_android.h
+++ b/content/browser/web_contents/web_contents_android.h
@@ -107,12 +107,6 @@
       const base::android::JavaParamRef<jobject>& obj);
   void ExitFullscreen(JNIEnv* env,
                       const base::android::JavaParamRef<jobject>& obj);
-  void UpdateBrowserControlsState(
-      JNIEnv* env,
-      const base::android::JavaParamRef<jobject>& obj,
-      bool enable_hiding,
-      bool enable_showing,
-      bool animate);
   void ScrollFocusedEditableNodeIntoView(
       JNIEnv* env,
       const base::android::JavaParamRef<jobject>& obj);
diff --git a/content/browser/web_contents/web_contents_impl.cc b/content/browser/web_contents/web_contents_impl.cc
index 3db465b..94149dc 100644
--- a/content/browser/web_contents/web_contents_impl.cc
+++ b/content/browser/web_contents/web_contents_impl.cc
@@ -4654,7 +4654,6 @@
 void WebContentsImpl::RunJavaScriptDialog(RenderFrameHost* render_frame_host,
                                           const base::string16& message,
                                           const base::string16& default_prompt,
-                                          const GURL& frame_url,
                                           JavaScriptDialogType dialog_type,
                                           IPC::Message* reply_msg) {
   // Running a dialog causes an exit to webpage-initiated fullscreen.
@@ -4693,13 +4692,14 @@
 
   for (auto* handler : page_handlers) {
     handler->DidRunJavaScriptDialog(
-        frame_url, message, default_prompt, dialog_type,
+        render_frame_host->GetLastCommittedURL(), message, default_prompt,
+        dialog_type,
         base::BindOnce(&CloseDialogCallbackWrapper::Run, wrapper, false));
   }
 
   if (dialog_manager_) {
     dialog_manager_->RunJavaScriptDialog(
-        this, frame_url, dialog_type, message, default_prompt,
+        this, render_frame_host, dialog_type, message, default_prompt,
         base::BindOnce(&CloseDialogCallbackWrapper::Run, wrapper, false),
         &suppress_this_message);
   }
diff --git a/content/browser/web_contents/web_contents_impl.h b/content/browser/web_contents/web_contents_impl.h
index 6bf997b..12ec232 100644
--- a/content/browser/web_contents/web_contents_impl.h
+++ b/content/browser/web_contents/web_contents_impl.h
@@ -492,7 +492,6 @@
   void RunJavaScriptDialog(RenderFrameHost* render_frame_host,
                            const base::string16& message,
                            const base::string16& default_prompt,
-                           const GURL& frame_url,
                            JavaScriptDialogType dialog_type,
                            IPC::Message* reply_msg) override;
   void RunBeforeUnloadConfirm(RenderFrameHost* render_frame_host,
diff --git a/content/browser/web_contents/web_contents_impl_browsertest.cc b/content/browser/web_contents/web_contents_impl_browsertest.cc
index ac2f655..1331cfa3 100644
--- a/content/browser/web_contents/web_contents_impl_browsertest.cc
+++ b/content/browser/web_contents/web_contents_impl_browsertest.cc
@@ -1079,7 +1079,7 @@
   // JavaScriptDialogManager
 
   void RunJavaScriptDialog(WebContents* web_contents,
-                           const GURL& alerting_frame_url,
+                           RenderFrameHost* render_frame_host,
                            JavaScriptDialogType dialog_type,
                            const base::string16& message_text,
                            const base::string16& default_prompt_text,
diff --git a/content/browser/web_contents/web_contents_impl_unittest.cc b/content/browser/web_contents/web_contents_impl_unittest.cc
index 19c2ca3..955e633 100644
--- a/content/browser/web_contents/web_contents_impl_unittest.cc
+++ b/content/browser/web_contents/web_contents_impl_unittest.cc
@@ -2543,8 +2543,7 @@
   IPC::Message* dummy_message = new IPC::Message;
   contents()->RunJavaScriptDialog(
       main_test_rfh(), base::ASCIIToUTF16("This is an informative message"),
-      base::ASCIIToUTF16("OK"), kGURL, JAVASCRIPT_DIALOG_TYPE_ALERT,
-      dummy_message);
+      base::ASCIIToUTF16("OK"), JAVASCRIPT_DIALOG_TYPE_ALERT, dummy_message);
   EXPECT_TRUE(contents()->last_dialog_suppressed_);
 }
 
@@ -3491,7 +3490,7 @@
   // JavaScriptDialogManager
 
   void RunJavaScriptDialog(WebContents* web_contents,
-                           const GURL& alerting_frame_url,
+                           RenderFrameHost* render_frame_host,
                            JavaScriptDialogType dialog_type,
                            const base::string16& message_text,
                            const base::string16& default_prompt_text,
diff --git a/content/browser/web_contents/web_contents_view_android.cc b/content/browser/web_contents/web_contents_view_android.cc
index 2e0022b..e5664db 100644
--- a/content/browser/web_contents/web_contents_view_android.cc
+++ b/content/browser/web_contents/web_contents_view_android.cc
@@ -502,7 +502,7 @@
 }
 
 bool WebContentsViewAndroid::OnTouchEvent(const ui::MotionEventAndroid& event) {
-  if (event.GetAction() == ui::MotionEventAndroid::ACTION_DOWN)
+  if (event.GetAction() == ui::MotionEventAndroid::Action::DOWN)
     content_view_core_->OnTouchDown(event.GetJavaObject());
   return false;  // let the children handle the actual event.
 }
@@ -510,9 +510,9 @@
 bool WebContentsViewAndroid::OnMouseEvent(const ui::MotionEventAndroid& event) {
   // Hover events can be intercepted when in accessibility mode.
   auto action = event.GetAction();
-  if (action != ui::MotionEventAndroid::ACTION_HOVER_ENTER &&
-      action != ui::MotionEventAndroid::ACTION_HOVER_EXIT &&
-      action != ui::MotionEventAndroid::ACTION_HOVER_MOVE)
+  if (action != ui::MotionEventAndroid::Action::HOVER_ENTER &&
+      action != ui::MotionEventAndroid::Action::HOVER_EXIT &&
+      action != ui::MotionEventAndroid::Action::HOVER_MOVE)
     return false;
 
   auto* manager = static_cast<BrowserAccessibilityManagerAndroid*>(
diff --git a/content/browser/web_contents/web_contents_view_aura.cc b/content/browser/web_contents/web_contents_view_aura.cc
index 2a827e5..5d28247a 100644
--- a/content/browser/web_contents/web_contents_view_aura.cc
+++ b/content/browser/web_contents/web_contents_view_aura.cc
@@ -20,7 +20,6 @@
 #include "content/browser/download/drag_download_util.h"
 #include "content/browser/frame_host/interstitial_page_impl.h"
 #include "content/browser/frame_host/navigation_entry_impl.h"
-#include "content/browser/mus_util.h"
 #include "content/browser/renderer_host/dip_util.h"
 #include "content/browser/renderer_host/display_util.h"
 #include "content/browser/renderer_host/input/touch_selection_controller_client_aura.h"
@@ -70,6 +69,7 @@
 #include "ui/base/dragdrop/os_exchange_data.h"
 #include "ui/base/dragdrop/os_exchange_data_provider_factory.h"
 #include "ui/base/hit_test.h"
+#include "ui/base/ui_base_features.h"
 #include "ui/base/ui_base_switches_util.h"
 #include "ui/compositor/layer.h"
 #include "ui/display/display.h"
@@ -468,9 +468,9 @@
 
 WebContentsViewAura::WebContentsViewAura(WebContentsImpl* web_contents,
                                          WebContentsViewDelegate* delegate)
-    : is_mus_browser_plugin_guest_(web_contents->GetBrowserPluginGuest() !=
-                                       nullptr &&
-                                   (switches::IsMusHostingViz())),
+    : is_mus_browser_plugin_guest_(
+          web_contents->GetBrowserPluginGuest() != nullptr &&
+          base::FeatureList::IsEnabled(features::kMash)),
       web_contents_(web_contents),
       delegate_(delegate),
       current_drag_op_(blink::kWebDragOperationNone),
@@ -482,8 +482,7 @@
       current_overscroll_gesture_(OVERSCROLL_NONE),
       completed_overscroll_gesture_(OVERSCROLL_NONE),
       navigation_overlay_(nullptr),
-      init_rwhv_with_null_parent_for_testing_(false) {
-}
+      init_rwhv_with_null_parent_for_testing_(false) {}
 
 void WebContentsViewAura::SetDelegateForTesting(
     WebContentsViewDelegate* delegate) {
diff --git a/content/browser/web_contents/web_contents_view_guest.cc b/content/browser/web_contents/web_contents_view_guest.cc
index 775a67df..699570c 100644
--- a/content/browser/web_contents/web_contents_view_guest.cc
+++ b/content/browser/web_contents/web_contents_view_guest.cc
@@ -12,7 +12,6 @@
 #include "content/browser/browser_plugin/browser_plugin_guest.h"
 #include "content/browser/frame_host/interstitial_page_impl.h"
 #include "content/browser/frame_host/render_widget_host_view_guest.h"
-#include "content/browser/mus_util.h"
 #include "content/browser/renderer_host/display_util.h"
 #include "content/browser/renderer_host/render_view_host_factory.h"
 #include "content/browser/renderer_host/render_view_host_impl.h"
@@ -21,7 +20,7 @@
 #include "content/public/browser/web_contents_delegate.h"
 #include "content/public/common/context_menu_params.h"
 #include "content/public/common/drop_data.h"
-#include "ui/base/ui_base_switches_util.h"
+#include "ui/base/ui_base_features.h"
 #include "ui/gfx/geometry/point.h"
 #include "ui/gfx/geometry/rect.h"
 #include "ui/gfx/geometry/size.h"
@@ -73,14 +72,14 @@
   // view hierarchy. We add this view as embedder's child here.
   // This would go in WebContentsViewGuest::CreateView, but that is too early to
   // access embedder_web_contents(). Therefore, we do it here.
-  if (!switches::IsMusHostingViz())
+  if (!base::FeatureList::IsEnabled(features::kMash))
     parent_view->GetNativeView()->AddChild(platform_view_->GetNativeView());
 #endif  // defined(USE_AURA)
 }
 
 void WebContentsViewGuest::OnGuestDetached(WebContentsView* old_parent_view) {
 #if defined(USE_AURA)
-  if (!switches::IsMusHostingViz()) {
+  if (!base::FeatureList::IsEnabled(features::kMash)) {
     old_parent_view->GetNativeView()->RemoveChild(
         platform_view_->GetNativeView());
   }
diff --git a/content/browser/webauth/authenticator_impl.cc b/content/browser/webauth/authenticator_impl.cc
index cd37289..8fa89a1f 100644
--- a/content/browser/webauth/authenticator_impl.cc
+++ b/content/browser/webauth/authenticator_impl.cc
@@ -178,7 +178,8 @@
     webauth::mojom::PublicKeyCredentialCreationOptionsPtr options,
     MakeCredentialCallback callback) {
   if (u2f_request_) {
-    std::move(callback).Run(
+    InvokeCallbackAndCleanup(
+        std::move(callback),
         webauth::mojom::AuthenticatorStatus::PENDING_REQUEST, nullptr);
     return;
   }
@@ -189,23 +190,26 @@
   if (!HasValidEffectiveDomain(caller_origin)) {
     bad_message::ReceivedBadMessage(render_frame_host_->GetProcess(),
                                     bad_message::AUTH_INVALID_EFFECTIVE_DOMAIN);
-    std::move(callback).Run(webauth::mojom::AuthenticatorStatus::INVALID_DOMAIN,
-                            nullptr);
+    InvokeCallbackAndCleanup(
+        std::move(callback),
+        webauth::mojom::AuthenticatorStatus::INVALID_DOMAIN, nullptr);
     return;
   }
 
   if (!IsRelyingPartyIdValid(relying_party_id_, caller_origin)) {
     bad_message::ReceivedBadMessage(render_frame_host_->GetProcess(),
                                     bad_message::AUTH_INVALID_RELYING_PARTY);
-    std::move(callback).Run(webauth::mojom::AuthenticatorStatus::INVALID_DOMAIN,
-                            nullptr);
+    InvokeCallbackAndCleanup(
+        std::move(callback),
+        webauth::mojom::AuthenticatorStatus::INVALID_DOMAIN, nullptr);
     return;
   }
 
   // Check that at least one of the cryptographic parameters is supported.
   // Only ES256 is currently supported by U2F_V2.
   if (!HasValidAlgorithm(options->public_key_parameters)) {
-    std::move(callback).Run(
+    InvokeCallbackAndCleanup(
+        std::move(callback),
         webauth::mojom::AuthenticatorStatus::NOT_SUPPORTED_ERROR, nullptr);
     return;
   }
@@ -266,7 +270,8 @@
     webauth::mojom::PublicKeyCredentialRequestOptionsPtr options,
     GetAssertionCallback callback) {
   if (u2f_request_) {
-    std::move(callback).Run(
+    InvokeCallbackAndCleanup(
+        std::move(callback),
         webauth::mojom::AuthenticatorStatus::PENDING_REQUEST, nullptr);
     return;
   }
@@ -276,16 +281,18 @@
   if (!HasValidEffectiveDomain(caller_origin)) {
     bad_message::ReceivedBadMessage(render_frame_host_->GetProcess(),
                                     bad_message::AUTH_INVALID_EFFECTIVE_DOMAIN);
-    std::move(callback).Run(webauth::mojom::AuthenticatorStatus::INVALID_DOMAIN,
-                            nullptr);
+    InvokeCallbackAndCleanup(
+        std::move(callback),
+        webauth::mojom::AuthenticatorStatus::INVALID_DOMAIN, nullptr);
     return;
   }
 
   if (!IsRelyingPartyIdValid(options->relying_party_id, caller_origin)) {
     bad_message::ReceivedBadMessage(render_frame_host_->GetProcess(),
                                     bad_message::AUTH_INVALID_RELYING_PARTY);
-    std::move(callback).Run(webauth::mojom::AuthenticatorStatus::INVALID_DOMAIN,
-                            nullptr);
+    InvokeCallbackAndCleanup(
+        std::move(callback),
+        webauth::mojom::AuthenticatorStatus::INVALID_DOMAIN, nullptr);
     return;
   }
 
@@ -320,19 +327,24 @@
 void AuthenticatorImpl::OnRegisterResponse(
     device::U2fReturnCode status_code,
     base::Optional<device::RegisterResponseData> response_data) {
-  timer_->Stop();
-
   switch (status_code) {
     case device::U2fReturnCode::CONDITIONS_NOT_SATISFIED:
-      // Duplicate registration.
-      std::move(make_credential_response_callback_)
-          .Run(webauth::mojom::AuthenticatorStatus::NOT_ALLOWED_ERROR, nullptr);
-      break;
+      // Duplicate registration: the new credential would be created on an
+      // authenticator that already contains one of the credentials in
+      // |exclude_credentials|.
+      InvokeCallbackAndCleanup(
+          std::move(make_credential_response_callback_),
+          webauth::mojom::AuthenticatorStatus::NOT_ALLOWED_ERROR, nullptr);
+      return;
     case device::U2fReturnCode::FAILURE:
+      // The response from the authenticator was corrupted.
+      InvokeCallbackAndCleanup(
+          std::move(make_credential_response_callback_),
+          webauth::mojom::AuthenticatorStatus::NOT_ALLOWED_ERROR, nullptr);
+      return;
     case device::U2fReturnCode::INVALID_PARAMS:
-      std::move(make_credential_response_callback_)
-          .Run(webauth::mojom::AuthenticatorStatus::UNKNOWN_ERROR, nullptr);
-      break;
+      NOTREACHED();
+      return;
     case device::U2fReturnCode::SUCCESS:
       DCHECK(response_data.has_value());
 
@@ -350,12 +362,14 @@
       }
 
       response_data->EraseAttestationStatement();
-      std::move(make_credential_response_callback_)
-          .Run(webauth::mojom::AuthenticatorStatus::SUCCESS,
-               CreateMakeCredentialResponse(std::move(client_data_),
-                                            std::move(*response_data)));
+      InvokeCallbackAndCleanup(
+          std::move(make_credential_response_callback_),
+          webauth::mojom::AuthenticatorStatus::SUCCESS,
+          CreateMakeCredentialResponse(std::move(client_data_),
+                                       std::move(*response_data)));
+      return;
   }
-  Cleanup();
+  NOTREACHED();
 }
 
 void AuthenticatorImpl::OnRegisterResponseAttestationDecided(
@@ -365,58 +379,84 @@
          webauth::mojom::AttestationConveyancePreference::NONE);
 
   if (!attestation_permitted) {
-    std::move(make_credential_response_callback_)
-        .Run(webauth::mojom::AuthenticatorStatus::NOT_ALLOWED_ERROR, nullptr);
+    // To protect users from being identified without consent, we let the
+    // timeout run out.
+    // See https://w3c.github.io/webauthn/#sec-assertion-privacy.
+    return;
   } else {
-    std::move(make_credential_response_callback_)
-        .Run(webauth::mojom::AuthenticatorStatus::SUCCESS,
-             CreateMakeCredentialResponse(std::move(client_data_),
-                                          std::move(response_data)));
+    InvokeCallbackAndCleanup(
+        std::move(make_credential_response_callback_),
+        webauth::mojom::AuthenticatorStatus::SUCCESS,
+        CreateMakeCredentialResponse(std::move(client_data_),
+                                     std::move(response_data)));
   }
-
-  Cleanup();
 }
 
 void AuthenticatorImpl::OnSignResponse(
     device::U2fReturnCode status_code,
     base::Optional<device::SignResponseData> response_data) {
-  timer_->Stop();
   switch (status_code) {
     case device::U2fReturnCode::CONDITIONS_NOT_SATISFIED:
       // No authenticators contained the credential.
-      std::move(get_assertion_response_callback_)
-          .Run(webauth::mojom::AuthenticatorStatus::NOT_ALLOWED_ERROR, nullptr);
-      break;
+      InvokeCallbackAndCleanup(
+          std::move(get_assertion_response_callback_),
+          webauth::mojom::AuthenticatorStatus::NOT_ALLOWED_ERROR, nullptr);
+      return;
     case device::U2fReturnCode::FAILURE:
+      // The response from the authenticator was corrupted.
+      InvokeCallbackAndCleanup(
+          std::move(make_credential_response_callback_),
+          webauth::mojom::AuthenticatorStatus::NOT_ALLOWED_ERROR, nullptr);
+      return;
     case device::U2fReturnCode::INVALID_PARAMS:
-      std::move(get_assertion_response_callback_)
-          .Run(webauth::mojom::AuthenticatorStatus::UNKNOWN_ERROR, nullptr);
-      break;
+      NOTREACHED();
+      return;
     case device::U2fReturnCode::SUCCESS:
       DCHECK(response_data.has_value());
-      std::move(get_assertion_response_callback_)
-          .Run(webauth::mojom::AuthenticatorStatus::SUCCESS,
-               CreateGetAssertionResponse(std::move(client_data_),
-                                          std::move(*response_data)));
-      break;
+      InvokeCallbackAndCleanup(
+          std::move(get_assertion_response_callback_),
+          webauth::mojom::AuthenticatorStatus::SUCCESS,
+          CreateGetAssertionResponse(std::move(client_data_),
+                                     std::move(*response_data)));
+      return;
   }
-  Cleanup();
+  NOTREACHED();
 }
 
 void AuthenticatorImpl::OnTimeout() {
+  // TODO(crbug.com/814418): Add layout tests to verify timeouts are
+  // indistinguishable from NOT_ALLOWED_ERROR cases.
   DCHECK(make_credential_response_callback_ ||
          get_assertion_response_callback_);
   if (make_credential_response_callback_) {
-    std::move(make_credential_response_callback_)
-        .Run(webauth::mojom::AuthenticatorStatus::TIMED_OUT, nullptr);
+    InvokeCallbackAndCleanup(
+        std::move(make_credential_response_callback_),
+        webauth::mojom::AuthenticatorStatus::NOT_ALLOWED_ERROR, nullptr);
   } else if (get_assertion_response_callback_) {
-    std::move(get_assertion_response_callback_)
-        .Run(webauth::mojom::AuthenticatorStatus::TIMED_OUT, nullptr);
+    InvokeCallbackAndCleanup(
+        std::move(get_assertion_response_callback_),
+        webauth::mojom::AuthenticatorStatus::NOT_ALLOWED_ERROR, nullptr);
   }
+}
+
+void AuthenticatorImpl::InvokeCallbackAndCleanup(
+    MakeCredentialCallback callback,
+    webauth::mojom::AuthenticatorStatus status,
+    webauth::mojom::MakeCredentialAuthenticatorResponsePtr response) {
+  std::move(callback).Run(status, std::move(response));
+  Cleanup();
+}
+
+void AuthenticatorImpl::InvokeCallbackAndCleanup(
+    GetAssertionCallback callback,
+    webauth::mojom::AuthenticatorStatus status,
+    webauth::mojom::GetAssertionAuthenticatorResponsePtr response) {
+  std::move(callback).Run(status, std::move(response));
   Cleanup();
 }
 
 void AuthenticatorImpl::Cleanup() {
+  timer_->Stop();
   u2f_request_.reset();
   make_credential_response_callback_.Reset();
   get_assertion_response_callback_.Reset();
diff --git a/content/browser/webauth/authenticator_impl.h b/content/browser/webauth/authenticator_impl.h
index 97a5d49..520dbb2 100644
--- a/content/browser/webauth/authenticator_impl.h
+++ b/content/browser/webauth/authenticator_impl.h
@@ -87,6 +87,15 @@
 
   // Runs when timer expires and cancels all issued requests to a U2fDevice.
   void OnTimeout();
+
+  void InvokeCallbackAndCleanup(
+      MakeCredentialCallback callback,
+      webauth::mojom::AuthenticatorStatus status,
+      webauth::mojom::MakeCredentialAuthenticatorResponsePtr response);
+  void InvokeCallbackAndCleanup(
+      GetAssertionCallback callback,
+      webauth::mojom::AuthenticatorStatus status,
+      webauth::mojom::GetAssertionAuthenticatorResponsePtr response);
   void Cleanup();
 
   // Owns pipes to this Authenticator from |render_frame_host_|.
diff --git a/content/browser/webauth/authenticator_impl_unittest.cc b/content/browser/webauth/authenticator_impl_unittest.cc
index cc50955..7efecd8 100644
--- a/content/browser/webauth/authenticator_impl_unittest.cc
+++ b/content/browser/webauth/authenticator_impl_unittest.cc
@@ -449,7 +449,7 @@
   base::RunLoop().RunUntilIdle();
   task_runner->FastForwardBy(base::TimeDelta::FromMinutes(1));
   cb.WaitForCallback();
-  EXPECT_EQ(webauth::mojom::AuthenticatorStatus::TIMED_OUT,
+  EXPECT_EQ(webauth::mojom::AuthenticatorStatus::NOT_ALLOWED_ERROR,
             cb.GetResponseStatus());
 }
 
@@ -505,7 +505,7 @@
   base::RunLoop().RunUntilIdle();
   task_runner->FastForwardBy(base::TimeDelta::FromMinutes(1));
   cb.WaitForCallback();
-  EXPECT_EQ(webauth::mojom::AuthenticatorStatus::TIMED_OUT,
+  EXPECT_EQ(webauth::mojom::AuthenticatorStatus::NOT_ALLOWED_ERROR,
             cb.GetResponseStatus());
 }
 }  // namespace content
diff --git a/content/browser/webrtc/webrtc_capture_from_element_browsertest.cc b/content/browser/webrtc/webrtc_capture_from_element_browsertest.cc
index 3560e1e..aa0ac27 100644
--- a/content/browser/webrtc/webrtc_capture_from_element_browsertest.cc
+++ b/content/browser/webrtc/webrtc_capture_from_element_browsertest.cc
@@ -145,9 +145,13 @@
 #if defined(OS_MACOSX)
 #define MAYBE_CaptureFromCanvas2DHandlesContextLoss \
   DISABLED_CaptureFromCanvas2DHandlesContextLoss
+#define MAYBE_CaptureFromOpaqueCanvas2DHandlesContextLoss \
+  DISABLED_CaptureFromOpaqueCanvas2DHandlesContextLoss
 #else
 #define MAYBE_CaptureFromCanvas2DHandlesContextLoss \
   CaptureFromCanvas2DHandlesContextLoss
+#define MAYBE_CaptureFromOpaqueCanvas2DHandlesContextLoss \
+  CaptureFromOpaqueCanvas2DHandlesContextLoss
 #endif  // defined(OS_MACOSX)
 IN_PROC_BROWSER_TEST_F(WebRtcCaptureFromElementBrowserTest,
                        MAYBE_CaptureFromCanvas2DHandlesContextLoss) {
@@ -156,7 +160,7 @@
 }
 
 IN_PROC_BROWSER_TEST_F(WebRtcCaptureFromElementBrowserTest,
-                       CaptureFromOpaqueCanvas2DHandlesContextLoss) {
+                       MAYBE_CaptureFromOpaqueCanvas2DHandlesContextLoss) {
   MakeTypicalCall("testCanvas2DContextLoss(false);",
                   kCanvasCaptureColorTestHtmlFile);
 }
diff --git a/content/child/child_thread_impl.h b/content/child/child_thread_impl.h
index 54bf250..fa8049f 100644
--- a/content/child/child_thread_impl.h
+++ b/content/child/child_thread_impl.h
@@ -32,7 +32,7 @@
 #include "services/resource_coordinator/public/cpp/tracing/chrome_trace_event_agent.h"
 
 #if defined(OS_WIN)
-#include "content/common/font_cache_win.mojom.h"
+#include "content/public/common/font_cache_win.mojom.h"
 #endif
 
 namespace base {
diff --git a/content/child/runtime_features.cc b/content/child/runtime_features.cc
index 8124ca93..efb7b58 100644
--- a/content/child/runtime_features.cc
+++ b/content/child/runtime_features.cc
@@ -196,10 +196,9 @@
   if (command_line.HasSwitch(switches::kReducedReferrerGranularity))
     WebRuntimeFeatures::EnableReducedReferrerGranularity(true);
 
-  if (base::FeatureList::IsEnabled(features::kRootLayerScrolling) ||
-      command_line.HasSwitch(switches::kRootLayerScrolls)) {
-    WebRuntimeFeatures::EnableRootLayerScrolling(true);
-  }
+  WebRuntimeFeatures::EnableRootLayerScrolling(
+      base::FeatureList::IsEnabled(features::kRootLayerScrolling) ||
+      enableExperimentalWebPlatformFeatures);
 
   if (command_line.HasSwitch(switches::kDisablePermissionsAPI))
     WebRuntimeFeatures::EnablePermissionsAPI(false);
diff --git a/content/common/BUILD.gn b/content/common/BUILD.gn
index 9ac207ee..2afda376 100644
--- a/content/common/BUILD.gn
+++ b/content/common/BUILD.gn
@@ -120,7 +120,6 @@
     "fileapi/file_system_messages.h",
     "fileapi/webblob_messages.h",
     "font_cache_dispatcher_win.cc",
-    "font_cache_dispatcher_win.h",
     "font_config_ipc_linux.cc",
     "font_config_ipc_linux.h",
     "font_list.cc",
@@ -589,10 +588,7 @@
   ]
 
   if (is_win) {
-    sources += [
-      "dwrite_font_proxy.mojom",
-      "font_cache_win.mojom",
-    ]
+    sources += [ "dwrite_font_proxy.mojom" ]
   }
 
   import_dirs = [ "//mojo/services" ]
diff --git a/content/common/content_param_traits.cc b/content/common/content_param_traits.cc
index 9afacce4..fbe9007 100644
--- a/content/common/content_param_traits.cc
+++ b/content/common/content_param_traits.cc
@@ -15,7 +15,7 @@
 #include "third_party/WebKit/public/common/message_port/message_port_channel.h"
 #include "third_party/WebKit/public/common/message_port/transferable_message.h"
 #include "ui/accessibility/ax_modes.h"
-#include "ui/base/ui_base_switches_util.h"
+#include "ui/base/ui_base_features.h"
 #include "ui/events/blink/web_input_event_traits.h"
 
 namespace IPC {
@@ -142,7 +142,7 @@
 void ParamTraits<content::FrameMsg_ViewChanged_Params>::Write(
     base::Pickle* m,
     const param_type& p) {
-  DCHECK(switches::IsMusHostingViz() ||
+  DCHECK(base::FeatureList::IsEnabled(features::kMash) ||
          (p.frame_sink_id.has_value() && p.frame_sink_id->is_valid()));
   WriteParam(m, p.frame_sink_id);
 }
@@ -153,7 +153,7 @@
     param_type* r) {
   if (!ReadParam(m, iter, &(r->frame_sink_id)))
     return false;
-  if (!switches::IsMusHostingViz() &&
+  if (!base::FeatureList::IsEnabled(features::kMash) &&
       (!r->frame_sink_id || !r->frame_sink_id->is_valid())) {
     NOTREACHED();
     return false;
diff --git a/content/common/font_cache_dispatcher_win.cc b/content/common/font_cache_dispatcher_win.cc
index 2f6e592..9337781 100644
--- a/content/common/font_cache_dispatcher_win.cc
+++ b/content/common/font_cache_dispatcher_win.cc
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "content/common/font_cache_dispatcher_win.h"
+#include "content/public/common/font_cache_dispatcher_win.h"
 
 #include <map>
 #include <memory>
diff --git a/content/common/view_messages.h b/content/common/view_messages.h
index 0d22109..4d4d304a 100644
--- a/content/common/view_messages.h
+++ b/content/common/view_messages.h
@@ -528,16 +528,6 @@
 // Fetches complete rendered content of a web page as plain text.
 IPC_MESSAGE_ROUTED0(ViewMsg_GetRenderedText)
 
-#if defined(OS_ANDROID)
-// Notifies the renderer whether hiding/showing the browser controls is enabled
-// and whether or not to animate to the proper state.
-IPC_MESSAGE_ROUTED3(ViewMsg_UpdateBrowserControlsState,
-                    bool /* enable_hiding */,
-                    bool /* enable_showing */,
-                    bool /* animate */)
-
-#endif
-
 IPC_MESSAGE_ROUTED0(ViewMsg_SelectWordAroundCaret)
 
 // Sent by the browser to ask the renderer to redraw. Robust to events that can
diff --git a/content/ppapi_plugin/ppapi_thread.cc b/content/ppapi_plugin/ppapi_thread.cc
index 26fc4a9..2f8a017 100644
--- a/content/ppapi_plugin/ppapi_thread.cc
+++ b/content/ppapi_plugin/ppapi_thread.cc
@@ -54,6 +54,7 @@
 #include "services/service_manager/public/cpp/connector.h"
 #include "services/ui/public/interfaces/constants.mojom.h"
 #include "third_party/WebKit/public/web/WebKit.h"
+#include "ui/base/ui_base_features.h"
 #include "ui/base/ui_base_switches.h"
 #include "ui/base/ui_features.h"
 
@@ -92,15 +93,6 @@
 
 #endif
 
-static bool IsRunningWithMus() {
-#if BUILDFLAG(ENABLE_MUS)
-  const base::CommandLine* cmdline = base::CommandLine::ForCurrentProcess();
-  return cmdline->HasSwitch(switches::kMus);
-#else
-  return false;
-#endif
-}
-
 namespace content {
 
 typedef int32_t (*InitializeBrokerFunc)
@@ -131,7 +123,7 @@
   // allocator.
   if (!command_line.HasSwitch(switches::kSingleProcess)) {
     discardable_memory::mojom::DiscardableSharedMemoryManagerPtr manager_ptr;
-    if (IsRunningWithMus()) {
+    if (features::IsMusEnabled()) {
 #if defined(USE_AURA)
       GetServiceManagerConnection()->GetConnector()->BindInterface(
           ui::mojom::kServiceName, &manager_ptr);
diff --git a/content/public/android/java/src/org/chromium/content/browser/ContentViewCoreImpl.java b/content/public/android/java/src/org/chromium/content/browser/ContentViewCoreImpl.java
index 2eecc73b..dea96cc 100644
--- a/content/public/android/java/src/org/chromium/content/browser/ContentViewCoreImpl.java
+++ b/content/public/android/java/src/org/chromium/content/browser/ContentViewCoreImpl.java
@@ -292,6 +292,8 @@
         SelectionPopupControllerImpl controller = SelectionPopupControllerImpl.create(
                 mContext, windowAndroid, webContents, containerView);
         controller.setActionModeCallback(ActionModeCallbackHelper.EMPTY_CALLBACK);
+        addWindowAndroidChangedObserver(controller);
+
         setContainerView(containerView);
         mRenderCoordinates = mWebContents.getRenderCoordinates();
         mRenderCoordinates.setDeviceScaleFactor(dipScale, windowAndroid.getContext().get());
diff --git a/content/public/android/java/src/org/chromium/content/browser/selection/SelectionPopupControllerImpl.java b/content/public/android/java/src/org/chromium/content/browser/selection/SelectionPopupControllerImpl.java
index 78989a57..ca6e6ce 100644
--- a/content/public/android/java/src/org/chromium/content/browser/selection/SelectionPopupControllerImpl.java
+++ b/content/public/android/java/src/org/chromium/content/browser/selection/SelectionPopupControllerImpl.java
@@ -41,6 +41,7 @@
 import org.chromium.base.metrics.RecordUserAction;
 import org.chromium.content.R;
 import org.chromium.content.browser.ContentClassFactory;
+import org.chromium.content.browser.WindowAndroidChangedObserver;
 import org.chromium.content.browser.WindowEventObserver;
 import org.chromium.content.browser.webcontents.WebContentsImpl;
 import org.chromium.content.browser.webcontents.WebContentsUserData;
@@ -61,8 +62,8 @@
  */
 @JNINamespace("content")
 @TargetApi(Build.VERSION_CODES.M)
-public class SelectionPopupControllerImpl
-        extends ActionModeCallbackHelper implements SelectionPopupController, WindowEventObserver {
+public class SelectionPopupControllerImpl extends ActionModeCallbackHelper
+        implements SelectionPopupController, WindowEventObserver, WindowAndroidChangedObserver {
     private static final String TAG = "SelectionPopupCtlr"; // 20 char limit
 
     /**
@@ -522,6 +523,13 @@
         }
     }
 
+    // WindowAndroidChangedObserver
+
+    @Override
+    public void onWindowAndroidChanged(WindowAndroid newWindowAndroid) {
+        mWindowAndroid = newWindowAndroid;
+    }
+
     // WindowEventObserver
 
     @Override
diff --git a/content/public/android/java/src/org/chromium/content/browser/webcontents/WebContentsImpl.java b/content/public/android/java/src/org/chromium/content/browser/webcontents/WebContentsImpl.java
index 751f3ba..5ffa5f5 100644
--- a/content/public/android/java/src/org/chromium/content/browser/webcontents/WebContentsImpl.java
+++ b/content/public/android/java/src/org/chromium/content/browser/webcontents/WebContentsImpl.java
@@ -399,13 +399,6 @@
     }
 
     @Override
-    public void updateBrowserControlsState(
-            boolean enableHiding, boolean enableShowing, boolean animate) {
-        nativeUpdateBrowserControlsState(
-                mNativeWebContentsAndroid, enableHiding, enableShowing, animate);
-    }
-
-    @Override
     public void scrollFocusedEditableNodeIntoView() {
         // The native side keeps track of whether the zoom and scroll actually occurred. It is
         // more efficient to do it this way and sometimes fire an unnecessary message rather
@@ -779,8 +772,6 @@
     private native boolean nativeFocusLocationBarByDefault(long nativeWebContentsAndroid);
     private native boolean nativeIsRenderWidgetHostViewReady(long nativeWebContentsAndroid);
     private native void nativeExitFullscreen(long nativeWebContentsAndroid);
-    private native void nativeUpdateBrowserControlsState(long nativeWebContentsAndroid,
-            boolean enableHiding, boolean enableShowing, boolean animate);
     private native void nativeScrollFocusedEditableNodeIntoView(long nativeWebContentsAndroid);
     private native void nativeSelectWordAroundCaret(long nativeWebContentsAndroid);
     private native void nativeAdjustSelectionByCharacterOffset(long nativeWebContentsAndroid,
diff --git a/content/public/android/java/src/org/chromium/content_public/browser/WebContents.java b/content/public/android/java/src/org/chromium/content_public/browser/WebContents.java
index 616e4bbb..630f5b0 100644
--- a/content/public/android/java/src/org/chromium/content_public/browser/WebContents.java
+++ b/content/public/android/java/src/org/chromium/content_public/browser/WebContents.java
@@ -246,15 +246,6 @@
     void exitFullscreen();
 
     /**
-     * Changes whether hiding the browser controls is enabled.
-     *
-     * @param enableHiding Whether hiding the browser controls should be enabled or not.
-     * @param enableShowing Whether showing the browser controls should be enabled or not.
-     * @param animate Whether the transition should be animated or not.
-     */
-    void updateBrowserControlsState(boolean enableHiding, boolean enableShowing, boolean animate);
-
-    /**
      * Brings the Editable to the visible area while IME is up to make easier for inputing text.
      */
     void scrollFocusedEditableNodeIntoView();
diff --git a/content/public/app/content_main.h b/content/public/app/content_main.h
index 230001c..b3dd9ede 100644
--- a/content/public/app/content_main.h
+++ b/content/public/app/content_main.h
@@ -15,10 +15,6 @@
 #include <windows.h>
 #endif
 
-#if defined(USE_AURA)
-#include "ui/aura/env.h"
-#endif
-
 namespace base {
 namespace mac {
 class ScopedNSAutoreleasePool;
@@ -61,13 +57,6 @@
   // BrowserMainParts has been created and before PreEarlyInitialization().
   CreatedMainPartsClosure* created_main_parts_closure = nullptr;
 
-#if defined(USE_AURA)
-  aura::Env::Mode env_mode = aura::Env::Mode::LOCAL;
-#endif
-
-  // If true a DiscardableSharedMemoryManager is created.
-  bool create_discardable_memory = true;
-
 #if defined(OS_MACOSX)
   // The outermost autorelease pool to pass to main entry points.
   base::mac::ScopedNSAutoreleasePool* autorelease_pool = nullptr;
diff --git a/content/public/browser/javascript_dialog_manager.h b/content/public/browser/javascript_dialog_manager.h
index 734dcf2..1aec6b2 100644
--- a/content/public/browser/javascript_dialog_manager.h
+++ b/content/public/browser/javascript_dialog_manager.h
@@ -30,7 +30,7 @@
   // Displays a JavaScript dialog. |did_suppress_message| will not be nil; if
   // |true| is returned in it, the caller will handle faking the reply.
   virtual void RunJavaScriptDialog(WebContents* web_contents,
-                                   const GURL& alerting_frame_url,
+                                   RenderFrameHost* render_frame_host,
                                    JavaScriptDialogType dialog_type,
                                    const base::string16& message_text,
                                    const base::string16& default_prompt_text,
diff --git a/content/public/browser/navigation_controller.h b/content/public/browser/navigation_controller.h
index 3bd12f4..a42220b 100644
--- a/content/public/browser/navigation_controller.h
+++ b/content/public/browser/navigation_controller.h
@@ -468,7 +468,7 @@
   virtual void PruneAllButLastCommitted() = 0;
 
   // Removes all navigation entries matching |deletionPredicate| except the last
-  // commited entry.
+  // committed entry.
   // Callers must ensure |CanPruneAllButLastCommitted| returns true before
   // calling this, or it will crash.
   virtual void DeleteNavigationEntries(
diff --git a/content/public/browser/web_contents_observer.h b/content/public/browser/web_contents_observer.h
index ca08f2f34..6025bd5e 100644
--- a/content/public/browser/web_contents_observer.h
+++ b/content/public/browser/web_contents_observer.h
@@ -278,7 +278,7 @@
   // This normally happens as a result of a new navigation. It will be
   // followed by a NavigationEntryCommitted() call for the new page that
   // caused the pruning. It could also be a result of removing an item from
-  // the list to delete history or fix up after interstitials.
+  // the list to fix up after interstitials.
   virtual void NavigationListPruned(const PrunedDetails& pruned_details) {}
 
   // Invoked when NavigationEntries have been deleted because of a history
diff --git a/content/public/common/BUILD.gn b/content/public/common/BUILD.gn
index d5f15b42..fa3bbde 100644
--- a/content/public/common/BUILD.gn
+++ b/content/public/common/BUILD.gn
@@ -146,6 +146,7 @@
     "file_chooser_file_info.h",
     "file_chooser_params.cc",
     "file_chooser_params.h",
+    "font_cache_dispatcher_win.h",
     "frame_navigate_params.cc",
     "frame_navigate_params.h",
     "injection_test_mac.h",
@@ -372,6 +373,10 @@
     "window_container_type.mojom",
   ]
 
+  if (is_win) {
+    sources += [ "font_cache_win.mojom" ]
+  }
+
   public_deps = [
     ":resource_type_bindings",
     "//mojo/common:common_custom_types",
diff --git a/content/public/common/content_features.cc b/content/public/common/content_features.cc
index 0cbc4d3b..ab03b3e5 100644
--- a/content/public/common/content_features.cc
+++ b/content/public/common/content_features.cc
@@ -288,7 +288,7 @@
 
 // Use common overflow scroll mechanism for frames. See http://crbug.com/417782.
 const base::Feature kRootLayerScrolling{"RootLayerScrolling",
-                                        base::FEATURE_DISABLED_BY_DEFAULT};
+                                        base::FEATURE_ENABLED_BY_DEFAULT};
 
 // Run video capture service in the Browser process as opposed to a dedicated
 // utility process
diff --git a/content/public/common/content_switches.cc b/content/public/common/content_switches.cc
index abf423ae..051eeae4 100644
--- a/content/public/common/content_switches.cc
+++ b/content/public/common/content_switches.cc
@@ -735,9 +735,6 @@
 const char kReducedReferrerGranularity[] =
   "reduced-referrer-granularity";
 
-// Handles frame scrolls via the root RenderLayer instead of the FrameView.
-const char kRootLayerScrolls[]              = "root-layer-scrolls";
-
 // Enables native memory sampling profiler with a given rate (default 128 KiB).
 const char kSamplingHeapProfiler[]          = "sampling-heap-profiler";
 
diff --git a/content/public/common/content_switches.h b/content/public/common/content_switches.h
index d381acb3..e94c7b3 100644
--- a/content/public/common/content_switches.h
+++ b/content/public/common/content_switches.h
@@ -209,7 +209,6 @@
 CONTENT_EXPORT extern const char kRendererProcess[];
 CONTENT_EXPORT extern const char kRendererProcessLimit[];
 CONTENT_EXPORT extern const char kRendererStartupDialog[];
-CONTENT_EXPORT extern const char kRootLayerScrolls[];
 extern const char kSandboxIPCProcess[];
 CONTENT_EXPORT extern const char kSavePreviousDocumentResources[];
 extern const char kShowPaintRects[];
diff --git a/content/common/font_cache_dispatcher_win.h b/content/public/common/font_cache_dispatcher_win.h
similarity index 87%
rename from content/common/font_cache_dispatcher_win.h
rename to content/public/common/font_cache_dispatcher_win.h
index 29eaa48..c046fda 100644
--- a/content/common/font_cache_dispatcher_win.h
+++ b/content/public/common/font_cache_dispatcher_win.h
@@ -9,7 +9,8 @@
 
 #include "base/macros.h"
 #include "base/memory/singleton.h"
-#include "content/common/font_cache_win.mojom.h"
+#include "content/common/content_export.h"
+#include "content/public/common/font_cache_win.mojom.h"
 
 namespace service_manager {
 struct BindSourceInfo;
@@ -20,7 +21,7 @@
 // Dispatches messages used for font caching on Windows. This is needed because
 // Windows can't load fonts into its kernel cache in sandboxed processes. So the
 // sandboxed process asks the browser process to do this for it.
-class FontCacheDispatcher : public mojom::FontCacheWin {
+class CONTENT_EXPORT FontCacheDispatcher : public mojom::FontCacheWin {
  public:
   FontCacheDispatcher();
   ~FontCacheDispatcher() override;
diff --git a/content/common/font_cache_win.mojom b/content/public/common/font_cache_win.mojom
similarity index 100%
rename from content/common/font_cache_win.mojom
rename to content/public/common/font_cache_win.mojom
diff --git a/content/public/common/main_function_params.h b/content/public/common/main_function_params.h
index f88205ed..a2c6c9e 100644
--- a/content/public/common/main_function_params.h
+++ b/content/public/common/main_function_params.h
@@ -13,10 +13,6 @@
 #include "base/command_line.h"
 #include "build/build_config.h"
 
-#if defined(USE_AURA)
-#include "ui/aura/env.h"
-#endif
-
 #if defined(OS_WIN)
 namespace sandbox {
 struct SandboxInterfaceInfo;
@@ -48,13 +44,6 @@
   bool zygote_child = false;
 #endif
 
-#if defined(USE_AURA)
-  aura::Env::Mode env_mode = aura::Env::Mode::LOCAL;
-#endif
-
-  // Whether DiscardableSharedMemoryManager should be created.
-  bool create_discardable_memory = true;
-
   // TODO(sky): fix ownership of these tasks. MainFunctionParams should really
   // be passed as an r-value, at which point these can be unique_ptrs. For the
   // time ownership is passed with MainFunctionParams (meaning these are deleted
diff --git a/content/public/test/android/javatests/src/org/chromium/content/browser/test/mock/MockWebContents.java b/content/public/test/android/javatests/src/org/chromium/content/browser/test/mock/MockWebContents.java
index a288bbf..445cb893 100644
--- a/content/public/test/android/javatests/src/org/chromium/content/browser/test/mock/MockWebContents.java
+++ b/content/public/test/android/javatests/src/org/chromium/content/browser/test/mock/MockWebContents.java
@@ -160,10 +160,6 @@
     public void exitFullscreen() {}
 
     @Override
-    public void updateBrowserControlsState(
-            boolean enableHiding, boolean enableShowing, boolean animate) {}
-
-    @Override
     public void scrollFocusedEditableNodeIntoView() {}
 
     @Override
diff --git a/content/public/test/test_launcher.cc b/content/public/test/test_launcher.cc
index d2d4a64..7556ba45 100644
--- a/content/public/test/test_launcher.cc
+++ b/content/public/test/test_launcher.cc
@@ -41,7 +41,7 @@
 #include "content/public/test/browser_test.h"
 #include "net/base/escape.h"
 #include "testing/gtest/include/gtest/gtest.h"
-#include "ui/base/ui_base_switches.h"
+#include "ui/base/ui_base_features.h"
 #include "ui/base/ui_features.h"
 
 #if defined(OS_POSIX)
@@ -557,13 +557,6 @@
   DoRunTests(test_launcher, test_names);
 }
 
-void PrepareToRunTestSuite(const base::CommandLine& command_line) {
-#if BUILDFLAG(ENABLE_MUS)
-  if (command_line.HasSwitch(switches::kMus))
-    g_params->env_mode = aura::Env::Mode::MUS;
-#endif
-}
-
 }  // namespace
 
 const char kHelpFlag[]   = "help";
@@ -627,7 +620,6 @@
       command_line->HasSwitch(base::kGTestListTestsFlag) ||
       command_line->HasSwitch(base::kGTestHelpFlag)) {
     g_params = &params;
-    PrepareToRunTestSuite(*command_line);
     return launcher_delegate->RunTestSuite(argc, argv);
   }
 
diff --git a/content/renderer/BUILD.gn b/content/renderer/BUILD.gn
index 2f9b5a6..b8d844a 100644
--- a/content/renderer/BUILD.gn
+++ b/content/renderer/BUILD.gn
@@ -265,8 +265,6 @@
     "manifest/manifest_parser.h",
     "manifest/manifest_uma_util.cc",
     "manifest/manifest_uma_util.h",
-    "mash_util.cc",
-    "mash_util.h",
     "media/android/media_player_renderer_client.cc",
     "media/android/media_player_renderer_client.h",
     "media/android/media_player_renderer_client_factory.cc",
diff --git a/content/renderer/accessibility/aom_content_ax_tree.cc b/content/renderer/accessibility/aom_content_ax_tree.cc
index be0d0a1..a4a71e4 100644
--- a/content/renderer/accessibility/aom_content_ax_tree.cc
+++ b/content/renderer/accessibility/aom_content_ax_tree.cc
@@ -55,6 +55,20 @@
   }
 }
 
+ax::mojom::FloatAttribute GetCorrespondingAXAttribute(
+    blink::WebAOMFloatAttribute attr) {
+  switch (attr) {
+    case blink::WebAOMFloatAttribute::AOM_ATTR_VALUE_MIN:
+      return ax::mojom::FloatAttribute::kMinValueForRange;
+    case blink::WebAOMFloatAttribute::AOM_ATTR_VALUE_MAX:
+      return ax::mojom::FloatAttribute::kMaxValueForRange;
+    case blink::WebAOMFloatAttribute::AOM_ATTR_VALUE_NOW:
+      return ax::mojom::FloatAttribute::kValueForRange;
+    default:
+      return ax::mojom::FloatAttribute::kNone;
+  }
+}
+
 ax::mojom::StringAttribute GetCorrespondingAXAttribute(
     blink::WebAOMStringAttribute attr) {
   switch (attr) {
@@ -166,6 +180,17 @@
   return true;
 }
 
+bool AomContentAxTree::GetFloatAttributeForAXNode(
+    int32_t ax_id,
+    blink::WebAOMFloatAttribute attr,
+    float* out_param) {
+  ui::AXNode* node = tree_.GetFromId(ax_id);
+  if (!node)
+    return false;
+  ax::mojom::FloatAttribute ax_attr = GetCorrespondingAXAttribute(attr);
+  return node->data().GetFloatAttribute(ax_attr, out_param);
+}
+
 bool AomContentAxTree::GetStringAttributeForAXNode(
     int32_t ax_id,
     blink::WebAOMStringAttribute attr,
diff --git a/content/renderer/accessibility/aom_content_ax_tree.h b/content/renderer/accessibility/aom_content_ax_tree.h
index 2d8f7df7..0f5b787 100644
--- a/content/renderer/accessibility/aom_content_ax_tree.h
+++ b/content/renderer/accessibility/aom_content_ax_tree.h
@@ -32,6 +32,9 @@
   bool GetStringAttributeForAXNode(int32_t ax_id,
                                    blink::WebAOMStringAttribute,
                                    blink::WebString* out_param) override;
+  bool GetFloatAttributeForAXNode(int32_t ax_id,
+                                  blink::WebAOMFloatAttribute,
+                                  float* out_param) override;
   bool GetRoleForAXNode(int32_t ax_id, blink::WebString* out_param) override;
   bool GetCheckedStateForAXNode(int32_t ax_id,
                                 blink::WebString* out_param) override;
diff --git a/content/renderer/browser_plugin/browser_plugin.cc b/content/renderer/browser_plugin/browser_plugin.cc
index 36d7818..d850d931 100644
--- a/content/renderer/browser_plugin/browser_plugin.cc
+++ b/content/renderer/browser_plugin/browser_plugin.cc
@@ -32,7 +32,6 @@
 #include "content/renderer/child_frame_compositing_helper.h"
 #include "content/renderer/cursor_utils.h"
 #include "content/renderer/drop_data_builder.h"
-#include "content/renderer/mash_util.h"
 #include "content/renderer/render_thread_impl.h"
 #include "content/renderer/sad_plugin.h"
 #include "third_party/WebKit/public/platform/WebCoalescedInputEvent.h"
@@ -46,7 +45,7 @@
 #include "third_party/WebKit/public/web/WebLocalFrame.h"
 #include "third_party/WebKit/public/web/WebPluginContainer.h"
 #include "third_party/WebKit/public/web/WebView.h"
-#include "ui/base/ui_base_switches_util.h"
+#include "ui/base/ui_base_features.h"
 #include "ui/events/keycodes/keyboard_codes.h"
 
 #if defined(USE_AURA)
@@ -141,7 +140,7 @@
 void BrowserPlugin::OnSetChildFrameSurface(
     int browser_plugin_instance_id,
     const viz::SurfaceInfo& surface_info) {
-  if (!attached() || switches::IsMusHostingViz())
+  if (!attached() || base::FeatureList::IsEnabled(features::kMash))
     return;
 
   if (!enable_surface_synchronization_) {
@@ -284,7 +283,7 @@
     sent_resize_params_ = pending_resize_params_;
 
 #if defined(USE_AURA)
-  if (IsRunningWithMus() && mus_embedded_frame_) {
+  if (features::IsMusEnabled() && mus_embedded_frame_) {
     mus_embedded_frame_->SetWindowBounds(local_surface_id_,
                                          FrameRectInPixels());
   }
@@ -351,7 +350,7 @@
 void BrowserPlugin::OnSetMusEmbedToken(
     int instance_id,
     const base::UnguessableToken& embed_token) {
-  DCHECK(switches::IsMusHostingViz());
+  DCHECK(base::FeatureList::IsEnabled(features::kMash));
   if (!attached_) {
     pending_embed_token_ = embed_token;
   } else {
@@ -773,7 +772,7 @@
 void BrowserPlugin::OnMusEmbeddedFrameSinkIdAllocated(
     const viz::FrameSinkId& frame_sink_id) {
   // RendererWindowTreeClient should only call this when mus is hosting viz.
-  DCHECK(switches::IsMusHostingViz());
+  DCHECK(base::FeatureList::IsEnabled(features::kMash));
   OnGuestReady(browser_plugin_instance_id_, frame_sink_id);
 }
 #endif
diff --git a/content/renderer/mash_util.cc b/content/renderer/mash_util.cc
deleted file mode 100644
index 42df688..0000000
--- a/content/renderer/mash_util.cc
+++ /dev/null
@@ -1,22 +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 "content/renderer/mash_util.h"
-
-#include "base/command_line.h"
-#include "ui/base/ui_base_switches.h"
-#include "ui/base/ui_features.h"
-
-namespace content {
-
-bool IsRunningWithMus() {
-#if BUILDFLAG(ENABLE_MUS)
-  const base::CommandLine* cmdline = base::CommandLine::ForCurrentProcess();
-  return cmdline->HasSwitch(switches::kMus);
-#else
-  return false;
-#endif
-}
-
-}  // namespace content
diff --git a/content/renderer/mash_util.h b/content/renderer/mash_util.h
deleted file mode 100644
index 3933b73..0000000
--- a/content/renderer/mash_util.h
+++ /dev/null
@@ -1,14 +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 CONTENT_RENDERER_MASH_UTIL_H_
-#define CONTENT_RENDERER_MASH_UTIL_H_
-
-namespace content {
-
-bool IsRunningWithMus();
-
-}  // namespace content
-
-#endif  // CONTENT_RENDERER_MASH_UTIL_H_
diff --git a/content/renderer/media/media_factory.cc b/content/renderer/media/media_factory.cc
index 9aab78c..d938838 100644
--- a/content/renderer/media/media_factory.cc
+++ b/content/renderer/media/media_factory.cc
@@ -283,7 +283,10 @@
   scoped_refptr<base::SingleThreadTaskRunner>
       video_frame_compositor_task_runner;
   std::unique_ptr<blink::WebVideoFrameSubmitter> submitter;
-  if (base::FeatureList::IsEnabled(media::kUseSurfaceLayerForVideo)) {
+  bool use_surface_layer_for_video =
+      base::FeatureList::IsEnabled(media::kUseSurfaceLayerForVideo) &&
+      !RenderThreadImpl::current()->IsGpuCompositingDisabled();
+  if (use_surface_layer_for_video) {
     // TODO(lethalantidote): Use a separate task_runner. https://crbug/753605.
     video_frame_compositor_task_runner =
         render_thread->GetMediaThreadTaskRunner();
@@ -321,7 +324,8 @@
           enable_instant_source_buffer_gc, embedded_media_experience_enabled,
           std::move(metrics_provider),
           base::Bind(&blink::WebSurfaceLayerBridge::Create, layer_tree_view),
-          RenderThreadImpl::current()->SharedMainThreadContextProvider()));
+          RenderThreadImpl::current()->SharedMainThreadContextProvider(),
+          use_surface_layer_for_video));
 
   std::unique_ptr<media::VideoFrameCompositor> vfc =
       std::make_unique<media::VideoFrameCompositor>(
diff --git a/content/renderer/mus/renderer_window_tree_client.cc b/content/renderer/mus/renderer_window_tree_client.cc
index 5f817e53..eeea1feb 100644
--- a/content/renderer/mus/renderer_window_tree_client.cc
+++ b/content/renderer/mus/renderer_window_tree_client.cc
@@ -14,11 +14,10 @@
 #include "components/viz/client/hit_test_data_provider_draw_quad.h"
 #include "components/viz/client/local_surface_id_provider.h"
 #include "components/viz/common/features.h"
-#include "content/renderer/mash_util.h"
 #include "content/renderer/mus/mus_embedded_frame.h"
 #include "content/renderer/mus/mus_embedded_frame_delegate.h"
 #include "content/renderer/render_frame_proxy.h"
-#include "ui/base/ui_base_switches_util.h"
+#include "ui/base/ui_base_features.h"
 
 namespace content {
 
@@ -32,7 +31,7 @@
 
 // static
 void RendererWindowTreeClient::CreateIfNecessary(int routing_id) {
-  if (!IsRunningWithMus() || Get(routing_id))
+  if (!features::IsMusEnabled() || Get(routing_id))
     return;
   RendererWindowTreeClient* connection =
       new RendererWindowTreeClient(routing_id);
@@ -224,7 +223,7 @@
     const viz::FrameSinkId& frame_sink_id) {
   // When mus is not hosting viz FrameSinkIds come from the browser, so we
   // ignore them here.
-  if (!switches::IsMusHostingViz())
+  if (!base::FeatureList::IsEnabled(features::kMash))
     return;
 
   for (MusEmbeddedFrame* embedded_frame : embedded_frames_) {
@@ -325,7 +324,7 @@
 void RendererWindowTreeClient::OnWindowSurfaceChanged(
     ui::Id window_id,
     const viz::SurfaceInfo& surface_info) {
-  DCHECK(switches::IsMusHostingViz());
+  DCHECK(base::FeatureList::IsEnabled(features::kMash));
   for (MusEmbeddedFrame* embedded_frame : embedded_frames_) {
     if (embedded_frame->window_id_ == window_id) {
       embedded_frame->delegate_->OnMusEmbeddedFrameSurfaceChanged(surface_info);
diff --git a/content/renderer/render_frame_proxy.cc b/content/renderer/render_frame_proxy.cc
index ca1f8c5..e4f803c0 100644
--- a/content/renderer/render_frame_proxy.cc
+++ b/content/renderer/render_frame_proxy.cc
@@ -26,7 +26,6 @@
 #include "content/renderer/child_frame_compositing_helper.h"
 #include "content/renderer/frame_owner_properties.h"
 #include "content/renderer/loader/web_url_request_util.h"
-#include "content/renderer/mash_util.h"
 #include "content/renderer/render_frame_impl.h"
 #include "content/renderer/render_thread_impl.h"
 #include "content/renderer/render_view_impl.h"
@@ -44,7 +43,7 @@
 #include "third_party/WebKit/public/web/WebTriggeringEventInfo.h"
 #include "third_party/WebKit/public/web/WebUserGestureIndicator.h"
 #include "third_party/WebKit/public/web/WebView.h"
-#include "ui/base/ui_base_switches_util.h"
+#include "ui/base/ui_base_features.h"
 
 #if defined(USE_AURA)
 #include "content/renderer/mus/mus_embedded_frame.h"
@@ -241,7 +240,7 @@
   pending_resize_params_.screen_info = render_widget_->screen_info();
 
 #if defined(USE_AURA)
-  if (IsRunningWithMus()) {
+  if (features::IsMusEnabled()) {
     RendererWindowTreeClient* renderer_window_tree_client =
         RendererWindowTreeClient::Get(render_widget_->routing_id());
     // It's possible a MusEmbeddedFrame has already been scheduled for creation
@@ -466,7 +465,7 @@
     const FrameMsg_ViewChanged_Params& params) {
   crashed_ = false;
   // In mash the FrameSinkId comes from RendererWindowTreeClient.
-  if (!switches::IsMusHostingViz())
+  if (!base::FeatureList::IsEnabled(features::kMash))
     frame_sink_id_ = *params.frame_sink_id;
 
   // Resend the FrameRects and allocate a new viz::LocalSurfaceId when the view
@@ -782,7 +781,7 @@
 void RenderFrameProxy::OnMusEmbeddedFrameSinkIdAllocated(
     const viz::FrameSinkId& frame_sink_id) {
   // RendererWindowTreeClient should only call this when mus is hosting viz.
-  DCHECK(switches::IsMusHostingViz());
+  DCHECK(base::FeatureList::IsEnabled(features::kMash));
   frame_sink_id_ = frame_sink_id;
   // Resend the FrameRects and allocate a new viz::LocalSurfaceId when the view
   // changes.
diff --git a/content/renderer/render_thread_impl.cc b/content/renderer/render_thread_impl.cc
index 0e5832e4..942fb6bf 100644
--- a/content/renderer/render_thread_impl.cc
+++ b/content/renderer/render_thread_impl.cc
@@ -100,7 +100,6 @@
 #include "content/renderer/input/input_handler_manager.h"
 #include "content/renderer/input/main_thread_input_event_filter.h"
 #include "content/renderer/loader/resource_dispatcher.h"
-#include "content/renderer/mash_util.h"
 #include "content/renderer/media/audio_input_message_filter.h"
 #include "content/renderer/media/audio_message_filter.h"
 #include "content/renderer/media/audio_renderer_mixer_manager.h"
@@ -173,8 +172,8 @@
 #include "third_party/boringssl/src/include/openssl/evp.h"
 #include "third_party/skia/include/core/SkGraphics.h"
 #include "ui/base/layout.h"
+#include "ui/base/ui_base_features.h"
 #include "ui/base/ui_base_switches.h"
-#include "ui/base/ui_base_switches_util.h"
 #include "ui/display/display_switches.h"
 #include "ui/gl/gl_switches.h"
 
@@ -790,11 +789,11 @@
       base::BindRepeating(&CreateSingleSampleMetricsProvider,
                           message_loop()->task_runner(), GetConnector()));
 
-  gpu_ =
-      ui::Gpu::Create(GetConnector(),
-                      switches::IsMusHostingViz() ? ui::mojom::kServiceName
-                                                  : mojom::kBrowserServiceName,
-                      GetIOTaskRunner());
+  gpu_ = ui::Gpu::Create(GetConnector(),
+                         base::FeatureList::IsEnabled(features::kMash)
+                             ? ui::mojom::kServiceName
+                             : mojom::kBrowserServiceName,
+                         GetIOTaskRunner());
 
   viz::mojom::SharedBitmapAllocationNotifierPtr
       shared_bitmap_allocation_notifier_ptr;
@@ -897,7 +896,7 @@
   AddFilter((new CacheStorageMessageFilter(thread_safe_sender()))->GetFilter());
 
 #if defined(USE_AURA)
-  if (IsRunningWithMus())
+  if (features::IsMusEnabled())
     CreateRenderWidgetWindowTreeClientFactory(GetServiceManagerConnection());
 #endif
 
@@ -1065,7 +1064,7 @@
   categorized_worker_pool_->Start(num_raster_threads);
 
   discardable_memory::mojom::DiscardableSharedMemoryManagerPtr manager_ptr;
-  if (IsRunningWithMus()) {
+  if (features::IsMusEnabled()) {
 #if defined(USE_AURA)
     GetServiceManagerConnection()->GetConnector()->BindInterface(
         ui::mojom::kServiceName, &manager_ptr);
@@ -2117,7 +2116,7 @@
   }
 
 #if defined(USE_AURA)
-  if (switches::IsMusHostingViz()) {
+  if (base::FeatureList::IsEnabled(features::kMash)) {
     if (!RendererWindowTreeClient::Get(routing_id)) {
       callback.Run(nullptr);
       return;
diff --git a/content/renderer/render_view_impl.cc b/content/renderer/render_view_impl.cc
index 8d25c0dc..d60410be 100644
--- a/content/renderer/render_view_impl.cc
+++ b/content/renderer/render_view_impl.cc
@@ -1125,10 +1125,7 @@
     IPC_MESSAGE_HANDLER(PageMsg_AudioStateChanged, OnAudioStateChanged)
     IPC_MESSAGE_HANDLER(PageMsg_UpdateScreenInfo, OnUpdateScreenInfo)
 
-#if defined(OS_ANDROID)
-    IPC_MESSAGE_HANDLER(ViewMsg_UpdateBrowserControlsState,
-                        OnUpdateBrowserControlsState)
-#elif defined(OS_MACOSX)
+#if defined(OS_MACOSX)
     IPC_MESSAGE_HANDLER(ViewMsg_GetRenderedText,
                         OnGetRenderedText)
     IPC_MESSAGE_HANDLER(ViewMsg_Close, OnClose)
diff --git a/content/renderer/render_view_impl.h b/content/renderer/render_view_impl.h
index 9af43e5..1e13f50e 100644
--- a/content/renderer/render_view_impl.h
+++ b/content/renderer/render_view_impl.h
@@ -544,11 +544,7 @@
   void OnForceRedraw(const ui::LatencyInfo& latency_info);
   void OnSelectWordAroundCaret();
   void OnAudioStateChanged(bool is_audio_playing);
-#if defined(OS_ANDROID)
-  void OnUpdateBrowserControlsState(bool enable_hiding,
-                                    bool enable_showing,
-                                    bool animate);
-#elif defined(OS_MACOSX)
+#if defined(OS_MACOSX)
   void OnGetRenderedText();
 #endif
 
diff --git a/content/renderer/render_view_impl_android.cc b/content/renderer/render_view_impl_android.cc
index 75690e7..75a0c0b 100644
--- a/content/renderer/render_view_impl_android.cc
+++ b/content/renderer/render_view_impl_android.cc
@@ -30,25 +30,6 @@
   return static_cast<blink::WebBrowserControlsState>(state);
 }
 
-
-// TODO(mvanouwerkerk): Stop calling this code path and delete it.
-void RenderViewImpl::OnUpdateBrowserControlsState(bool enable_hiding,
-                                                  bool enable_showing,
-                                                  bool animate) {
-  // TODO(tedchoc): Investigate why messages are getting here before the
-  //                compositor has been initialized.
-  LOG_IF(WARNING, !compositor_)
-      << "OnUpdateBrowserControlsState was unhandled.";
-  BrowserControlsState constraints = BROWSER_CONTROLS_STATE_BOTH;
-  if (!enable_showing)
-    constraints = BROWSER_CONTROLS_STATE_HIDDEN;
-  if (!enable_hiding)
-    constraints = BROWSER_CONTROLS_STATE_SHOWN;
-  BrowserControlsState current = BROWSER_CONTROLS_STATE_BOTH;
-
-  UpdateBrowserControlsState(constraints, current, animate);
-}
-
 void RenderViewImpl::UpdateBrowserControlsState(
     BrowserControlsState constraints,
     BrowserControlsState current,
diff --git a/content/renderer/render_widget.cc b/content/renderer/render_widget.cc
index 382f960..c491764 100644
--- a/content/renderer/render_widget.cc
+++ b/content/renderer/render_widget.cc
@@ -57,7 +57,6 @@
 #include "content/renderer/input/input_handler_manager.h"
 #include "content/renderer/input/main_thread_event_queue.h"
 #include "content/renderer/input/widget_input_handler_manager.h"
-#include "content/renderer/mash_util.h"
 #include "content/renderer/pepper/pepper_plugin_instance_impl.h"
 #include "content/renderer/render_frame_impl.h"
 #include "content/renderer/render_frame_proxy.h"
@@ -99,7 +98,7 @@
 #include "third_party/WebKit/public/web/WebWidget.h"
 #include "third_party/skia/include/core/SkShader.h"
 #include "ui/base/clipboard/clipboard.h"
-#include "ui/base/ui_base_switches.h"
+#include "ui/base/ui_base_features.h"
 #include "ui/events/base_event_utils.h"
 #include "ui/gfx/geometry/point_conversions.h"
 #include "ui/gfx/geometry/rect_conversions.h"
@@ -435,7 +434,7 @@
   }
 #if defined(USE_AURA)
   RendererWindowTreeClient::CreateIfNecessary(routing_id_);
-  if (IsRunningWithMus())
+  if (features::IsMusEnabled())
     RendererWindowTreeClient::Get(routing_id_)->SetVisible(!is_hidden_);
 #endif
 }
@@ -2229,7 +2228,7 @@
   is_hidden_ = hidden;
 
 #if defined(USE_AURA)
-  if (IsRunningWithMus())
+  if (features::IsMusEnabled())
     RendererWindowTreeClient::Get(routing_id_)->SetVisible(!hidden);
 #endif
 
diff --git a/content/shell/browser/layout_test/layout_test_javascript_dialog_manager.cc b/content/shell/browser/layout_test/layout_test_javascript_dialog_manager.cc
index 7ee6a24..e110028 100644
--- a/content/shell/browser/layout_test/layout_test_javascript_dialog_manager.cc
+++ b/content/shell/browser/layout_test/layout_test_javascript_dialog_manager.cc
@@ -24,7 +24,7 @@
 
 void LayoutTestJavaScriptDialogManager::RunJavaScriptDialog(
     WebContents* web_contents,
-    const GURL& alerting_frame_url,
+    RenderFrameHost* render_frame_host,
     JavaScriptDialogType dialog_type,
     const base::string16& message_text,
     const base::string16& default_prompt_text,
diff --git a/content/shell/browser/layout_test/layout_test_javascript_dialog_manager.h b/content/shell/browser/layout_test/layout_test_javascript_dialog_manager.h
index 88973b6..64d93ce 100644
--- a/content/shell/browser/layout_test/layout_test_javascript_dialog_manager.h
+++ b/content/shell/browser/layout_test/layout_test_javascript_dialog_manager.h
@@ -21,7 +21,7 @@
 
   // JavaScriptDialogManager:
   void RunJavaScriptDialog(WebContents* web_contents,
-                           const GURL& origin_url,
+                           RenderFrameHost* render_frame_host,
                            JavaScriptDialogType dialog_type,
                            const base::string16& message_text,
                            const base::string16& default_prompt_text,
diff --git a/content/shell/browser/shell_javascript_dialog_manager.cc b/content/shell/browser/shell_javascript_dialog_manager.cc
index a31b0575..eabcda1 100644
--- a/content/shell/browser/shell_javascript_dialog_manager.cc
+++ b/content/shell/browser/shell_javascript_dialog_manager.cc
@@ -24,7 +24,7 @@
 
 void ShellJavaScriptDialogManager::RunJavaScriptDialog(
     WebContents* web_contents,
-    const GURL& alerting_frame_url,
+    RenderFrameHost* render_frame_host,
     JavaScriptDialogType dialog_type,
     const base::string16& message_text,
     const base::string16& default_prompt_text,
@@ -47,7 +47,7 @@
   }
 
   base::string16 new_message_text =
-      url_formatter::FormatUrl(alerting_frame_url) +
+      url_formatter::FormatUrl(render_frame_host->GetLastCommittedURL()) +
       base::ASCIIToUTF16("\n\n") + message_text;
   gfx::NativeWindow parent_window = web_contents->GetTopLevelNativeWindow();
 
diff --git a/content/shell/browser/shell_javascript_dialog_manager.h b/content/shell/browser/shell_javascript_dialog_manager.h
index ce1b0ab3..6740cff3 100644
--- a/content/shell/browser/shell_javascript_dialog_manager.h
+++ b/content/shell/browser/shell_javascript_dialog_manager.h
@@ -24,7 +24,7 @@
 
   // JavaScriptDialogManager:
   void RunJavaScriptDialog(WebContents* web_contents,
-                           const GURL& origin_url,
+                           RenderFrameHost* render_frame_host,
                            JavaScriptDialogType dialog_type,
                            const base::string16& message_text,
                            const base::string16& default_prompt_text,
diff --git a/content/test/data/accessibility/aria/aria-cell-expected-blink.txt b/content/test/data/accessibility/aria/aria-cell-expected-blink.txt
index 3f568ca..276bc458 100644
--- a/content/test/data/accessibility/aria/aria-cell-expected-blink.txt
+++ b/content/test/data/accessibility/aria/aria-cell-expected-blink.txt
@@ -13,4 +13,7 @@
 ++++++++++inlineTextBox name='Chrome'
 ++++++cell selectable name='Blink'
 ++++++++staticText name='Blink'
-++++++++++inlineTextBox name='Blink'
\ No newline at end of file
+++++++++++inlineTextBox name='Blink'
+++++column
+++++column
+++++tableHeaderContainer
\ No newline at end of file
diff --git a/content/test/data/accessibility/aria/aria-cell-expected-win.txt b/content/test/data/accessibility/aria/aria-cell-expected-win.txt
index 942cd45..7c5be31e 100644
--- a/content/test/data/accessibility/aria/aria-cell-expected-win.txt
+++ b/content/test/data/accessibility/aria/aria-cell-expected-win.txt
@@ -9,4 +9,7 @@
 ++++++ROLE_SYSTEM_CELL name='Chrome' SELECTABLE xml-roles:cell
 ++++++++ROLE_SYSTEM_STATICTEXT name='Chrome'
 ++++++ROLE_SYSTEM_CELL name='Blink' SELECTABLE xml-roles:cell
-++++++++ROLE_SYSTEM_STATICTEXT name='Blink'
\ No newline at end of file
+++++++++ROLE_SYSTEM_STATICTEXT name='Blink'
+++++ROLE_SYSTEM_COLUMN
+++++ROLE_SYSTEM_COLUMN
+++++IA2_ROLE_SECTION
\ No newline at end of file
diff --git a/content/test/data/accessibility/aria/aria-table-expected-blink.txt b/content/test/data/accessibility/aria/aria-table-expected-blink.txt
index 3f568ca..c3838da 100644
--- a/content/test/data/accessibility/aria/aria-table-expected-blink.txt
+++ b/content/test/data/accessibility/aria/aria-table-expected-blink.txt
@@ -13,4 +13,8 @@
 ++++++++++inlineTextBox name='Chrome'
 ++++++cell selectable name='Blink'
 ++++++++staticText name='Blink'
-++++++++++inlineTextBox name='Blink'
\ No newline at end of file
+++++++++++inlineTextBox name='Blink'
+++++column
+++++column
+++++tableHeaderContainer
+<-- End-of-file -->
\ No newline at end of file
diff --git a/content/test/data/accessibility/aria/aria-table-expected-mac.txt b/content/test/data/accessibility/aria/aria-table-expected-mac.txt
index 8145bb2..4f7181ae 100644
--- a/content/test/data/accessibility/aria/aria-table-expected-mac.txt
+++ b/content/test/data/accessibility/aria/aria-table-expected-mac.txt
@@ -10,3 +10,6 @@
 ++++++++AXStaticText AXRoleDescription='text' AXValue='Chrome'
 ++++++AXCell AXRoleDescription='cell'
 ++++++++AXStaticText AXRoleDescription='text' AXValue='Blink'
+++++AXColumn AXRoleDescription='column'
+++++AXColumn AXRoleDescription='column'
+++++AXGroup AXRoleDescription='group'
\ No newline at end of file
diff --git a/content/test/data/accessibility/aria/aria-table-expected-win.txt b/content/test/data/accessibility/aria/aria-table-expected-win.txt
index 79ffc24..a38cafc3 100644
--- a/content/test/data/accessibility/aria/aria-table-expected-win.txt
+++ b/content/test/data/accessibility/aria/aria-table-expected-win.txt
@@ -1,12 +1,15 @@
 ROLE_SYSTEM_DOCUMENT READONLY FOCUSABLE
-++ROLE_SYSTEM_TABLE xml-roles:table
+++ROLE_SYSTEM_TABLE xml-roles:table table_rows=2 table_columns=2
 ++++ROLE_SYSTEM_ROW xml-roles:row
-++++++ROLE_SYSTEM_COLUMNHEADER name='Browser' xml-roles:columnheader
+++++++ROLE_SYSTEM_COLUMNHEADER name='Browser' xml-roles:columnheader table-cell-index:0
 ++++++++ROLE_SYSTEM_STATICTEXT name='Browser'
-++++++ROLE_SYSTEM_COLUMNHEADER name='Rendering Engine' xml-roles:columnheader
+++++++ROLE_SYSTEM_COLUMNHEADER name='Rendering Engine' xml-roles:columnheader table-cell-index:1
 ++++++++ROLE_SYSTEM_STATICTEXT name='Rendering Engine'
 ++++ROLE_SYSTEM_ROW xml-roles:row
-++++++ROLE_SYSTEM_CELL name='Chrome' xml-roles:cell
+++++++ROLE_SYSTEM_CELL name='Chrome' xml-roles:cell table-cell-index:2
 ++++++++ROLE_SYSTEM_STATICTEXT name='Chrome'
-++++++ROLE_SYSTEM_CELL name='Blink' xml-roles:cell
+++++++ROLE_SYSTEM_CELL name='Blink' xml-roles:cell table-cell-index:3
 ++++++++ROLE_SYSTEM_STATICTEXT name='Blink'
+++++ROLE_SYSTEM_COLUMN
+++++ROLE_SYSTEM_COLUMN
+++++IA2_ROLE_SECTION
diff --git a/content/test/data/accessibility/aria/aria-table.html b/content/test/data/accessibility/aria/aria-table.html
index 79de857..e1536bf 100644
--- a/content/test/data/accessibility/aria/aria-table.html
+++ b/content/test/data/accessibility/aria/aria-table.html
@@ -1,6 +1,7 @@
 <!--
 @MAC-ALLOW:AXRole*
 @WIN-ALLOW:xml-roles*
+@WIN-ALLOW:table*
 -->
 <!DOCTYPE html>
 <html>
diff --git a/content/test/gpu/generate_buildbot_json.py b/content/test/gpu/generate_buildbot_json.py
index 398f74d..b3a71df 100755
--- a/content/test/gpu/generate_buildbot_json.py
+++ b/content/test/gpu/generate_buildbot_json.py
@@ -991,9 +991,7 @@
         "media_unittests",
         "media_blink_unittests",
         "mojo_common_unittests",
-        "mojo_public_bindings_unittests",
-        "mojo_public_system_unittests",
-        "mojo_system_unittests",
+        "mojo_unittests",
         "nacl_loader_unittests",
         "net_unittests",
         "pdf_unittests",
diff --git a/device/gamepad/gamepad_device_mac.mm b/device/gamepad/gamepad_device_mac.mm
index 26c2dfd1..82f2906 100644
--- a/device/gamepad/gamepad_device_mac.mm
+++ b/device/gamepad/gamepad_device_mac.mm
@@ -124,7 +124,7 @@
       }
     } else if (IOHIDElementGetType(element) == kIOHIDElementTypeInput_Misc) {
       uint32_t axis_index = usage - kAxisMinimumUsageNumber;
-      if (axis_index < Gamepad::kAxesLengthCap) {
+      if (axis_index < Gamepad::kAxesLengthCap && !axis_elements_[axis_index]) {
         axis_elements_[axis_index] = element;
         gamepad->axes_length = std::max(gamepad->axes_length, axis_index + 1);
       } else {
diff --git a/device/vr/oculus/oculus_render_loop.cc b/device/vr/oculus/oculus_render_loop.cc
index 96d68f0..fa23fa1 100644
--- a/device/vr/oculus/oculus_render_loop.cc
+++ b/device/vr/oculus/oculus_render_loop.cc
@@ -84,15 +84,18 @@
       layer.ColorTexture[0] = texture_swap_chain_;
       DCHECK(source_size_.width() % 2 == 0);
       layer.Viewport[0] = {
-          {source_size_.width() * left_bounds_.x(),
-           source_size_.height() * left_bounds_.y()},
-          {source_size_.width() * left_bounds_.width(),
-           source_size_.height() * left_bounds_.height()}};  // left viewport
+          // Left viewport.
+          {static_cast<int>(source_size_.width() * left_bounds_.x()),
+           static_cast<int>(source_size_.height() * left_bounds_.y())},
+          {static_cast<int>(source_size_.width() * left_bounds_.width()),
+           static_cast<int>(source_size_.height() * left_bounds_.height())}};
+
       layer.Viewport[1] = {
-          {source_size_.width() * right_bounds_.x(),
-           source_size_.height() * right_bounds_.y()},
-          {source_size_.width() * right_bounds_.width(),
-           source_size_.height() * right_bounds_.height()}};  // right viewport
+          // Right viewport.
+          {static_cast<int>(source_size_.width() * right_bounds_.x()),
+           static_cast<int>(source_size_.height() * right_bounds_.y())},
+          {static_cast<int>(source_size_.width() * right_bounds_.width()),
+           static_cast<int>(source_size_.height() * right_bounds_.height())}};
       ovrHmdDesc hmdDesc = ovr_GetHmdDesc(session_);
       layer.Fov[0] = hmdDesc.DefaultEyeFov[0];
       layer.Fov[1] = hmdDesc.DefaultEyeFov[1];
diff --git a/docs/ozone_overview.md b/docs/ozone_overview.md
index 85cbbe38..2c835403 100644
--- a/docs/ozone_overview.md
+++ b/docs/ozone_overview.md
@@ -169,7 +169,7 @@
 Then to run for example the X11 platform:
 
 ``` shell
-./out/OzoneLinuxDesktop/chrome --ozone-platform=x11 --mus
+./out/OzoneLinuxDesktop/chrome --ozone-platform=x11 --enable-features=Mus
 ```
 
 ### GN Configuration notes
@@ -257,7 +257,7 @@
 ``` shell
 gn args out/OzoneWayland --args="use_ozone=true enable_mus=true"
 ninja -C out/OzoneWayland chrome
-./out/OzoneWayland/chrome --ozone-platform=wayland --mus
+./out/OzoneWayland/chrome --ozone-platform=wayland --enable-features=Mus
 ```
 
 ### Caca
diff --git a/docs/security/faq.md b/docs/security/faq.md
index 11178201..efc562d 100644
--- a/docs/security/faq.md
+++ b/docs/security/faq.md
@@ -98,14 +98,15 @@
 Other timing attacks can be mitigated via clever design changes. For instance,
 [Issue 544765](https://crbug.com/544765) describes an attack whereby an attacker
 can probe for the presence of HSTS rules (set by prior site visits) by timing
-the load of resources with URLs "fixed-up" by HSTS. HSTS rules are shared
-between regular browsing and Incognito mode, making the attack more interesting.
-The attack was mitigated by changing Content-Security-Policy such that secure
-URLs will match rules demanding non-secure HTTP urls, a fix that has also proven
-useful to help to unblock migrations to HTTPS. Similarly,
-[Issue 707071](https://crbug.com/707071) describes a timing attack in which an
-attacker could determine what Android applications are installed; the attack was
-mitigated by introducing randomness in the execution time of the affected API.
+the load of resources with URLs "fixed-up" by HSTS. Prior to Chrome 64, HSTS
+rules [were shared](https://crbug.com/774643) between regular browsing and
+Incognito mode, making the attack more interesting. The attack was mitigated by
+changing Content-Security-Policy such that secure URLs will match rules
+demanding non-secure HTTP urls, a fix that has also proven useful to help to
+unblock migrations to HTTPS. Similarly, [Issue 707071](https://crbug.com/707071)
+describes a timing attack in which an attacker could determine what Android
+applications are installed; the attack was mitigated by introducing randomness
+in the execution time of the affected API.
 
 <a name="TOC-What-are-the-security-and-privacy-guarantees-of-Incognito-mode-"></a>
 ## What are the security and privacy guarantees of Incognito mode?
@@ -202,9 +203,10 @@
 *    Assume everything you do on a public computer will become, well, public.
      You have no control over the operating system or other software on the
      machine, and there is no reason to trust the integrity of it.
-*    If you must use such a computer, consider using an incognito mode window,
-     to avoid persisting credentials. This, however, provides no protection
-     when the system is already compromised as above.
+*    If you must use such a computer, use Incognito mode and close all Incognito
+     windows when you are done browsing to limit the amount of data you leave
+     behind. Note that Incognito mode **provides no protection** if the system has
+     already been compromised as described above.
 
 <a name="TOC-Why-aren-t-compromised-infected-machines-in-Chrome-s-threat-model-"></a>
 ## Why aren't compromised/infected machines in Chrome's threat model?
@@ -236,11 +238,11 @@
 <a name="TOC-Does-entering-JavaScript:-URLs-in-the-URL-bar-or-running-script-in-the-developer-tools-mean-there-s-an-XSS-vulnerability-"></a>
 ## Does entering JavaScript: URLs in the URL bar or running script in the developer tools mean there's an XSS vulnerability?
 
-No. Chrome does not attempt to prevent the user from knowingly running script
-against loaded documents, either by entering script in the Developer Tools
-console or by typing a JavaScript: URI into the URL bar. Chrome and other
-browsers do undertake some efforts to prevent *paste* of script URLs in the URL
-bar (to limit
+[No](https://crbug.com/81697). Chrome does not attempt to prevent the user from
+knowingly running script against loaded documents, either by entering script in
+the Developer Tools console or by typing a JavaScript: URI into the URL bar.
+Chrome and other browsers do undertake some efforts to prevent *paste* of script
+URLs in the URL bar (to limit
 [social-engineering](https://blogs.msdn.microsoft.com/ieinternals/2011/05/19/socially-engineered-xss-attacks/))
 but users are otherwise free to invoke script against pages using either the URL
 bar or the DevTools console.
diff --git a/extensions/browser/api/cast_channel/DEPS b/extensions/browser/api/cast_channel/DEPS
index 44ac277a..b7f785b 100644
--- a/extensions/browser/api/cast_channel/DEPS
+++ b/extensions/browser/api/cast_channel/DEPS
@@ -3,3 +3,9 @@
   "+components/cast_channel",
   "+third_party/zlib",
 ]
+
+specific_include_rules = {
+  "cast_channel_apitest\.cc": [
+    "+chrome/browser/media/router",
+  ],
+}
diff --git a/extensions/browser/api/cast_channel/cast_channel_apitest.cc b/extensions/browser/api/cast_channel/cast_channel_apitest.cc
index 14c4243..7bae881b 100644
--- a/extensions/browser/api/cast_channel/cast_channel_apitest.cc
+++ b/extensions/browser/api/cast_channel/cast_channel_apitest.cc
@@ -10,6 +10,8 @@
 #include "build/build_config.h"
 #include "chrome/browser/extensions/extension_apitest.h"
 #include "chrome/browser/extensions/extension_function_test_utils.h"
+#include "chrome/browser/media/router/providers/cast/dual_media_sink_service.h"
+#include "chrome/browser/media/router/test/noop_dual_media_sink_service.h"
 #include "chrome/browser/ui/browser.h"
 #include "components/cast_channel/cast_socket.h"
 #include "components/cast_channel/cast_socket_service.h"
@@ -90,6 +92,13 @@
         extensions::switches::kWhitelistedExtensionID, kTestExtensionId);
   }
 
+  void SetUp() override {
+    // Stub out DualMediaSinkService so it does not interfere with the test.
+    media_router::DualMediaSinkService::SetInstanceForTest(
+        new media_router::NoopDualMediaSinkService());
+    ExtensionApiTest::SetUp();
+  }
+
   void SetUpMockCastSocket() {
     extensions::CastChannelAPI* api = GetApi();
 
diff --git a/extensions/browser/api/web_request/web_request_api.cc b/extensions/browser/api/web_request/web_request_api.cc
index 59cacd7..6a0ff04 100644
--- a/extensions/browser/api/web_request/web_request_api.cc
+++ b/extensions/browser/api/web_request/web_request_api.cc
@@ -431,7 +431,7 @@
   *factory_request = mojo::MakeRequest(&target_factory_info);
 
   auto proxy = base::MakeRefCounted<WebRequestProxyingURLLoaderFactory>(
-      browser_context_, info_map_);
+      frame->GetProcess()->GetBrowserContext(), info_map_);
   proxies_.emplace(proxy.get(), proxy);
   BrowserThread::PostTask(
       BrowserThread::IO, FROM_HERE,
diff --git a/extensions/browser/api/web_request/web_request_event_details.cc b/extensions/browser/api/web_request/web_request_event_details.cc
index 5629841..db118e8 100644
--- a/extensions/browser/api/web_request/web_request_event_details.cc
+++ b/extensions/browser/api/web_request/web_request_event_details.cc
@@ -166,7 +166,8 @@
   if (extension_info_map && initiator_) {
     int tab_id = -1;
     dict_.GetInteger(keys::kTabIdKey, &tab_id);
-    if (WebRequestPermissions::CanExtensionAccessInitiator(
+    if (initiator_->unique() ||
+        WebRequestPermissions::CanExtensionAccessInitiator(
             extension_info_map, extension_id, initiator_, tab_id,
             crosses_incognito)) {
       result->SetString(keys::kInitiatorKey, initiator_->Serialize());
diff --git a/extensions/browser/guest_view/web_view/javascript_dialog_helper.cc b/extensions/browser/guest_view/web_view/javascript_dialog_helper.cc
index 4e018f6..72daaf7 100644
--- a/extensions/browser/guest_view/web_view/javascript_dialog_helper.cc
+++ b/extensions/browser/guest_view/web_view/javascript_dialog_helper.cc
@@ -44,7 +44,7 @@
 
 void JavaScriptDialogHelper::RunJavaScriptDialog(
     content::WebContents* web_contents,
-    const GURL& alerting_frame_url,
+    content::RenderFrameHost* render_frame_host,
     content::JavaScriptDialogType dialog_type,
     const base::string16& message_text,
     const base::string16& default_prompt_text,
@@ -57,7 +57,8 @@
                          base::UTF16ToUTF8(message_text));
   request_info.SetString(webview::kMessageType,
                          JavaScriptDialogTypeToString(dialog_type));
-  request_info.SetString(guest_view::kUrl, alerting_frame_url.spec());
+  request_info.SetString(guest_view::kUrl,
+                         render_frame_host->GetLastCommittedURL().spec());
   WebViewPermissionHelper* web_view_permission_helper =
       WebViewPermissionHelper::FromWebContents(web_contents);
   web_view_permission_helper->RequestPermission(
diff --git a/extensions/browser/guest_view/web_view/javascript_dialog_helper.h b/extensions/browser/guest_view/web_view/javascript_dialog_helper.h
index 26de5112..7f42b61 100644
--- a/extensions/browser/guest_view/web_view/javascript_dialog_helper.h
+++ b/extensions/browser/guest_view/web_view/javascript_dialog_helper.h
@@ -19,7 +19,7 @@
 
   // JavaScriptDialogManager implementation.
   void RunJavaScriptDialog(content::WebContents* web_contents,
-                           const GURL& alerting_frame_url,
+                           content::RenderFrameHost* render_frame_host,
                            content::JavaScriptDialogType dialog_type,
                            const base::string16& message_text,
                            const base::string16& default_prompt_text,
diff --git a/extensions/common/api/web_request.json b/extensions/common/api/web_request.json
index 9b21cb6..5453a99 100644
--- a/extensions/common/api/web_request.json
+++ b/extensions/common/api/web_request.json
@@ -204,7 +204,7 @@
               },
               "tabId": {"type": "integer", "description": "The ID of the tab in which the request takes place. Set to -1 if the request isn't related to a tab."},
               "type": {"$ref": "ResourceType", "description": "How the requested resource will be used."},
-              "initiator": {"type": "string", "optional": true, "description": "The origin where the request was initiated. This does not change through redirects."},
+              "initiator": {"type": "string", "optional": true, "description": "The origin where the request was initiated. This does not change through redirects. If this is an opaque origin, the string 'null' will be used."},
               "timeStamp": {"type": "number", "description": "The time when this signal is triggered, in milliseconds since the epoch."}
             }
           }
@@ -247,7 +247,7 @@
               "frameId": {"type": "integer", "description": "The value 0 indicates that the request happens in the main frame; a positive value indicates the ID of a subframe in which the request happens. If the document of a (sub-)frame is loaded (<code>type</code> is <code>main_frame</code> or <code>sub_frame</code>), <code>frameId</code> indicates the ID of this frame, not the ID of the outer frame. Frame IDs are unique within a tab."},
               "parentFrameId": {"type": "integer", "description": "ID of frame that wraps the frame which sent the request. Set to -1 if no parent frame exists."},
               "tabId": {"type": "integer", "description": "The ID of the tab in which the request takes place. Set to -1 if the request isn't related to a tab."},
-              "initiator": {"type": "string", "optional": true, "description": "The origin where the request was initiated. This does not change through redirects."},
+              "initiator": {"type": "string", "optional": true, "description": "The origin where the request was initiated. This does not change through redirects. If this is an opaque origin, the string 'null' will be used."},
               "type": {"$ref": "ResourceType", "description": "How the requested resource will be used."},
               "timeStamp": {"type": "number", "description": "The time when this signal is triggered, in milliseconds since the epoch."},
               "requestHeaders": {"$ref": "HttpHeaders", "optional": true, "description": "The HTTP request headers that are going to be sent out with this request."}
@@ -293,7 +293,7 @@
               "parentFrameId": {"type": "integer", "description": "ID of frame that wraps the frame which sent the request. Set to -1 if no parent frame exists."},
               "tabId": {"type": "integer", "description": "The ID of the tab in which the request takes place. Set to -1 if the request isn't related to a tab."},
               "type": {"$ref": "ResourceType", "description": "How the requested resource will be used."},
-              "initiator": {"type": "string", "optional": true, "description": "The origin where the request was initiated. This does not change through redirects."},
+              "initiator": {"type": "string", "optional": true, "description": "The origin where the request was initiated. This does not change through redirects. If this is an opaque origin, the string 'null' will be used."},
               "timeStamp": {"type": "number", "description": "The time when this signal is triggered, in milliseconds since the epoch."},
               "requestHeaders": {"$ref": "HttpHeaders", "optional": true, "description": "The HTTP request headers that have been sent out with this request."}
             }
@@ -333,7 +333,7 @@
               "parentFrameId": {"type": "integer", "description": "ID of frame that wraps the frame which sent the request. Set to -1 if no parent frame exists."},
               "tabId": {"type": "integer", "description": "The ID of the tab in which the request takes place. Set to -1 if the request isn't related to a tab."},
               "type": {"$ref": "ResourceType", "description": "How the requested resource will be used."},
-              "initiator": {"type": "string", "optional": true, "description": "The origin where the request was initiated. This does not change through redirects."},
+              "initiator": {"type": "string", "optional": true, "description": "The origin where the request was initiated. This does not change through redirects. If this is an opaque origin, the string 'null' will be used."},
               "timeStamp": {"type": "number", "description": "The time when this signal is triggered, in milliseconds since the epoch."},
               "statusLine": {"type": "string", "description": "HTTP status line of the response or the 'HTTP/0.9 200 OK' string for HTTP/0.9 responses (i.e., responses that lack a status line)."},
               "responseHeaders": {"$ref": "HttpHeaders", "optional": true, "description": "The HTTP response headers that have been received with this response."},
@@ -380,7 +380,7 @@
               "parentFrameId": {"type": "integer", "description": "ID of frame that wraps the frame which sent the request. Set to -1 if no parent frame exists."},
               "tabId": {"type": "integer", "description": "The ID of the tab in which the request takes place. Set to -1 if the request isn't related to a tab."},
               "type": {"$ref": "ResourceType", "description": "How the requested resource will be used."},
-              "initiator": {"type": "string", "optional": true, "description": "The origin where the request was initiated. This does not change through redirects."},
+              "initiator": {"type": "string", "optional": true, "description": "The origin where the request was initiated. This does not change through redirects. If this is an opaque origin, the string 'null' will be used."},
               "timeStamp": {"type": "number", "description": "The time when this signal is triggered, in milliseconds since the epoch."},
               "scheme": {"type": "string", "description": "The authentication scheme, e.g. Basic or Digest."},
               "realm": {"type": "string", "description": "The authentication realm provided by the server, if there is one.", "optional": true},
@@ -440,7 +440,7 @@
               "parentFrameId": {"type": "integer", "description": "ID of frame that wraps the frame which sent the request. Set to -1 if no parent frame exists."},
               "tabId": {"type": "integer", "description": "The ID of the tab in which the request takes place. Set to -1 if the request isn't related to a tab."},
               "type": {"$ref": "ResourceType", "description": "How the requested resource will be used."},
-              "initiator": {"type": "string", "optional": true, "description": "The origin where the request was initiated. This does not change through redirects."},
+              "initiator": {"type": "string", "optional": true, "description": "The origin where the request was initiated. This does not change through redirects. If this is an opaque origin, the string 'null' will be used."},
               "timeStamp": {"type": "number", "description": "The time when this signal is triggered, in milliseconds since the epoch."},
               "ip": {"type": "string", "optional": true, "description": "The server IP address that the request was actually sent to. Note that it may be a literal IPv6 address."},
               "fromCache": {"type": "boolean", "description": "Indicates if this response was fetched from disk cache."},
@@ -484,7 +484,7 @@
               "parentFrameId": {"type": "integer", "description": "ID of frame that wraps the frame which sent the request. Set to -1 if no parent frame exists."},
               "tabId": {"type": "integer", "description": "The ID of the tab in which the request takes place. Set to -1 if the request isn't related to a tab."},
               "type": {"$ref": "ResourceType", "description": "How the requested resource will be used."},
-              "initiator": {"type": "string", "optional": true, "description": "The origin where the request was initiated. This does not change through redirects."},
+              "initiator": {"type": "string", "optional": true, "description": "The origin where the request was initiated. This does not change through redirects. If this is an opaque origin, the string 'null' will be used."},
               "timeStamp": {"type": "number", "description": "The time when this signal is triggered, in milliseconds since the epoch."},
               "ip": {"type": "string", "optional": true, "description": "The server IP address that the request was actually sent to. Note that it may be a literal IPv6 address."},
               "fromCache": {"type": "boolean", "description": "Indicates if this response was fetched from disk cache."},
@@ -529,7 +529,7 @@
               "parentFrameId": {"type": "integer", "description": "ID of frame that wraps the frame which sent the request. Set to -1 if no parent frame exists."},
               "tabId": {"type": "integer", "description": "The ID of the tab in which the request takes place. Set to -1 if the request isn't related to a tab."},
               "type": {"$ref": "ResourceType", "description": "How the requested resource will be used."},
-              "initiator": {"type": "string", "optional": true, "description": "The origin where the request was initiated. This does not change through redirects."},
+              "initiator": {"type": "string", "optional": true, "description": "The origin where the request was initiated. This does not change through redirects. If this is an opaque origin, the string 'null' will be used."},
               "timeStamp": {"type": "number", "description": "The time when this signal is triggered, in milliseconds since the epoch."},
               "ip": {"type": "string", "optional": true, "description": "The server IP address that the request was actually sent to. Note that it may be a literal IPv6 address."},
               "fromCache": {"type": "boolean", "description": "Indicates if this response was fetched from disk cache."},
@@ -572,7 +572,7 @@
               "parentFrameId": {"type": "integer", "description": "ID of frame that wraps the frame which sent the request. Set to -1 if no parent frame exists."},
               "tabId": {"type": "integer", "description": "The ID of the tab in which the request takes place. Set to -1 if the request isn't related to a tab."},
               "type": {"$ref": "ResourceType", "description": "How the requested resource will be used."},
-              "initiator": {"type": "string", "optional": true, "description": "The origin where the request was initiated. This does not change through redirects."},
+              "initiator": {"type": "string", "optional": true, "description": "The origin where the request was initiated. This does not change through redirects. If this is an opaque origin, the string 'null' will be used."},
               "timeStamp": {"type": "number", "description": "The time when this signal is triggered, in milliseconds since the epoch."},
               "ip": {"type": "string", "optional": true, "description": "The server IP address that the request was actually sent to. Note that it may be a literal IPv6 address."},
               "fromCache": {"type": "boolean", "description": "Indicates if this response was fetched from disk cache."},
diff --git a/extensions/shell/BUILD.gn b/extensions/shell/BUILD.gn
index ae12352..0fb7ae2 100644
--- a/extensions/shell/BUILD.gn
+++ b/extensions/shell/BUILD.gn
@@ -131,6 +131,8 @@
     "browser/shell_display_info_provider.h",
     "browser/shell_extension_host_delegate.cc",
     "browser/shell_extension_host_delegate.h",
+    "browser/shell_extension_loader.cc",
+    "browser/shell_extension_loader.h",
     "browser/shell_extension_system.cc",
     "browser/shell_extension_system.h",
     "browser/shell_extension_system_factory.cc",
@@ -290,6 +292,7 @@
   sources = [
     "../test/extensions_unittests_main.cc",
     "browser/api/identity/identity_api_unittest.cc",
+    "browser/shell_extension_loader_unittest.cc",
     "browser/shell_keep_alive_requester_unittest.cc",
     "browser/shell_oauth2_token_service_unittest.cc",
     "browser/shell_prefs_unittest.cc",
diff --git a/extensions/shell/browser/shell_extension_loader.cc b/extensions/shell/browser/shell_extension_loader.cc
new file mode 100644
index 0000000..9d2997b
--- /dev/null
+++ b/extensions/shell/browser/shell_extension_loader.cc
@@ -0,0 +1,116 @@
+// 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 "extensions/shell/browser/shell_extension_loader.h"
+
+#include "base/files/file_path.h"
+#include "base/files/file_util.h"
+#include "base/logging.h"
+#include "extensions/browser/extension_prefs.h"
+#include "extensions/common/file_util.h"
+
+namespace extensions {
+
+using LoadErrorBehavior = ExtensionRegistrar::LoadErrorBehavior;
+
+namespace {
+
+scoped_refptr<const Extension> LoadUnpacked(
+    const base::FilePath& extension_dir) {
+  // app_shell only supports unpacked extensions.
+  // NOTE: If you add packed extension support consider removing the flag
+  // FOLLOW_SYMLINKS_ANYWHERE below. Packed extensions should not have symlinks.
+  if (!base::DirectoryExists(extension_dir)) {
+    LOG(ERROR) << "Extension directory not found: "
+               << extension_dir.AsUTF8Unsafe();
+    return nullptr;
+  }
+
+  int load_flags = Extension::FOLLOW_SYMLINKS_ANYWHERE;
+  std::string load_error;
+  scoped_refptr<Extension> extension = file_util::LoadExtension(
+      extension_dir, Manifest::COMMAND_LINE, load_flags, &load_error);
+  if (!extension.get()) {
+    LOG(ERROR) << "Loading extension at " << extension_dir.value()
+               << " failed with: " << load_error;
+    return nullptr;
+  }
+
+  // Log warnings.
+  if (extension->install_warnings().size()) {
+    LOG(WARNING) << "Warnings loading extension at " << extension_dir.value()
+                 << ":";
+    for (const auto& warning : extension->install_warnings())
+      LOG(WARNING) << warning.message;
+  }
+
+  return extension;
+}
+
+}  // namespace
+
+ShellExtensionLoader::ShellExtensionLoader(
+    content::BrowserContext* browser_context)
+    : browser_context_(browser_context),
+      extension_registrar_(browser_context, this) {}
+
+ShellExtensionLoader::~ShellExtensionLoader() = default;
+
+const Extension* ShellExtensionLoader::LoadExtension(
+    const base::FilePath& extension_dir) {
+  scoped_refptr<const Extension> extension = LoadUnpacked(extension_dir);
+  if (extension)
+    extension_registrar_.AddExtension(extension);
+
+  return extension.get();
+}
+
+void ShellExtensionLoader::PreAddExtension(const Extension* extension,
+                                           const Extension* old_extension) {
+  if (old_extension)
+    return;
+
+  // The extension might be disabled if a previous reload attempt failed. In
+  // that case, we want to remove that disable reason.
+  ExtensionPrefs* extension_prefs = ExtensionPrefs::Get(browser_context_);
+  if (extension_prefs->IsExtensionDisabled(extension->id()) &&
+      extension_prefs->HasDisableReason(extension->id(),
+                                        disable_reason::DISABLE_RELOAD)) {
+    extension_prefs->RemoveDisableReason(extension->id(),
+                                         disable_reason::DISABLE_RELOAD);
+    // Only re-enable the extension if there are no other disable reasons.
+    if (extension_prefs->GetDisableReasons(extension->id()) ==
+        disable_reason::DISABLE_NONE) {
+      extension_prefs->SetExtensionEnabled(extension->id());
+    }
+  }
+}
+
+void ShellExtensionLoader::PostActivateExtension(
+    scoped_refptr<const Extension> extension) {}
+
+void ShellExtensionLoader::PostDeactivateExtension(
+    scoped_refptr<const Extension> extension) {}
+
+void ShellExtensionLoader::LoadExtensionForReload(
+    const ExtensionId& extension_id,
+    const base::FilePath& path,
+    LoadErrorBehavior load_error_behavior) {
+  // TODO(michaelpg): Support reload.
+}
+
+bool ShellExtensionLoader::CanEnableExtension(const Extension* extension) {
+  return true;
+}
+
+bool ShellExtensionLoader::CanDisableExtension(const Extension* extension) {
+  // Extensions cannot be disabled by the user.
+  return false;
+}
+
+bool ShellExtensionLoader::ShouldBlockExtension(const Extension* extension) {
+  return false;
+}
+
+}  // namespace extensions
diff --git a/extensions/shell/browser/shell_extension_loader.h b/extensions/shell/browser/shell_extension_loader.h
new file mode 100644
index 0000000..9b88f9a
--- /dev/null
+++ b/extensions/shell/browser/shell_extension_loader.h
@@ -0,0 +1,64 @@
+// 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 EXTENSIONS_SHELL_BROWSER_SHELL_EXTENSION_LOADER_H_
+#define EXTENSIONS_SHELL_BROWSER_SHELL_EXTENSION_LOADER_H_
+
+#include <memory>
+#include <string>
+
+#include "base/macros.h"
+#include "base/memory/ref_counted.h"
+#include "base/memory/weak_ptr.h"
+#include "extensions/browser/extension_registrar.h"
+#include "extensions/common/extension_id.h"
+
+namespace base {
+class FilePath;
+}  // namespace base
+
+namespace content {
+class BrowserContext;
+}  // namespace content
+
+namespace extensions {
+
+class Extension;
+
+// Handles extension loading using ExtensionRegistrar.
+class ShellExtensionLoader : public ExtensionRegistrar::Delegate {
+ public:
+  explicit ShellExtensionLoader(content::BrowserContext* browser_context);
+  ~ShellExtensionLoader() override;
+
+  // Loads an unpacked extension from a directory synchronously. Returns the
+  // extension on success, or nullptr otherwise.
+  const Extension* LoadExtension(const base::FilePath& extension_dir);
+
+ private:
+  // ExtensionRegistrar::Delegate:
+  void PreAddExtension(const Extension* extension,
+                       const Extension* old_extension) override;
+  void PostActivateExtension(scoped_refptr<const Extension> extension) override;
+  void PostDeactivateExtension(
+      scoped_refptr<const Extension> extension) override;
+  void LoadExtensionForReload(
+      const ExtensionId& extension_id,
+      const base::FilePath& path,
+      ExtensionRegistrar::LoadErrorBehavior load_error_behavior) override;
+  bool CanEnableExtension(const Extension* extension) override;
+  bool CanDisableExtension(const Extension* extension) override;
+  bool ShouldBlockExtension(const Extension* extension) override;
+
+  content::BrowserContext* browser_context_;  // Not owned.
+
+  // Registers and unregisters extensions.
+  ExtensionRegistrar extension_registrar_;
+
+  DISALLOW_COPY_AND_ASSIGN(ShellExtensionLoader);
+};
+
+}  // namespace extensions
+
+#endif  // EXTENSIONS_SHELL_BROWSER_SHELL_EXTENSION_LOADER_H_
diff --git a/extensions/shell/browser/shell_extension_loader_unittest.cc b/extensions/shell/browser/shell_extension_loader_unittest.cc
new file mode 100644
index 0000000..4f986d61
--- /dev/null
+++ b/extensions/shell/browser/shell_extension_loader_unittest.cc
@@ -0,0 +1,144 @@
+// 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 "extensions/shell/browser/shell_extension_loader.h"
+
+#include <memory>
+#include <string>
+
+#include "base/macros.h"
+#include "base/path_service.h"
+#include "components/crx_file/id_util.h"
+#include "content/public/browser/browser_context.h"
+#include "content/public/test/test_browser_thread_bundle.h"
+#include "extensions/browser/disable_reason.h"
+#include "extensions/browser/extension_prefs.h"
+#include "extensions/browser/extension_registry.h"
+#include "extensions/browser/extensions_test.h"
+#include "extensions/browser/null_app_sorting.h"
+#include "extensions/browser/runtime_data.h"
+#include "extensions/browser/test_extensions_browser_client.h"
+#include "extensions/common/extension.h"
+#include "extensions/common/extension_id.h"
+#include "extensions/common/extension_paths.h"
+
+namespace extensions {
+
+namespace {
+
+class TestExtensionSystem : public MockExtensionSystem {
+ public:
+  explicit TestExtensionSystem(content::BrowserContext* context)
+      : MockExtensionSystem(context),
+        runtime_data_(ExtensionRegistry::Get(context)) {}
+  ~TestExtensionSystem() override = default;
+
+  RuntimeData* runtime_data() override { return &runtime_data_; }
+  AppSorting* app_sorting() override { return &app_sorting_; }
+
+ private:
+  RuntimeData runtime_data_;
+  NullAppSorting app_sorting_;
+
+  DISALLOW_COPY_AND_ASSIGN(TestExtensionSystem);
+};
+
+}  // namespace
+
+class ShellExtensionLoaderTest : public ExtensionsTest {
+ protected:
+  ShellExtensionLoaderTest()
+      : ExtensionsTest(std::make_unique<content::TestBrowserThreadBundle>()) {}
+  ~ShellExtensionLoaderTest() override = default;
+
+  void SetUp() override {
+    ExtensionsTest::SetUp();
+    extensions_browser_client()->set_extension_system_factory(&factory_);
+  }
+
+  // Returns the path to a test directory.
+  base::FilePath GetExtensionPath(const std::string& dir) {
+    base::FilePath test_data_dir;
+    PathService::Get(DIR_TEST_DATA, &test_data_dir);
+    return test_data_dir.AppendASCII(dir);
+  }
+
+  // Verifies the extension is correctly created and enabled.
+  void ExpectEnabled(const Extension* extension) {
+    ASSERT_TRUE(extension);
+    EXPECT_EQ(Manifest::COMMAND_LINE, extension->location());
+    ExtensionRegistry* registry = ExtensionRegistry::Get(browser_context());
+    EXPECT_TRUE(registry->enabled_extensions().Contains(extension->id()));
+    EXPECT_FALSE(registry->GetExtensionById(
+        extension->id(),
+        ExtensionRegistry::EVERYTHING & ~ExtensionRegistry::ENABLED));
+  }
+
+  // Verifies the extension is correctly created but disabled.
+  void ExpectDisabled(const Extension* extension) {
+    ASSERT_TRUE(extension);
+    EXPECT_EQ(Manifest::COMMAND_LINE, extension->location());
+    ExtensionRegistry* registry = ExtensionRegistry::Get(browser_context());
+    EXPECT_TRUE(registry->disabled_extensions().Contains(extension->id()));
+    EXPECT_FALSE(registry->GetExtensionById(
+        extension->id(),
+        ExtensionRegistry::EVERYTHING & ~ExtensionRegistry::DISABLED));
+  }
+
+ private:
+  MockExtensionSystemFactory<TestExtensionSystem> factory_;
+
+  DISALLOW_COPY_AND_ASSIGN(ShellExtensionLoaderTest);
+};
+
+// Tests loading an extension.
+TEST_F(ShellExtensionLoaderTest, Extension) {
+  ShellExtensionLoader loader(browser_context());
+
+  const Extension* extension =
+      loader.LoadExtension(GetExtensionPath("extension"));
+  ExpectEnabled(extension);
+}
+
+// Tests with a non-existent directory.
+TEST_F(ShellExtensionLoaderTest, NotFound) {
+  ShellExtensionLoader loader(browser_context());
+
+  const Extension* extension =
+      loader.LoadExtension(GetExtensionPath("nonexistent"));
+  ASSERT_FALSE(extension);
+}
+
+// Tests that the extension is added as enabled even if is disabled in
+// ExtensionPrefs. Unlike Chrome, AppShell doesn't have a UI surface for
+// re-enabling a disabled extension.
+TEST_F(ShellExtensionLoaderTest, LoadAfterReloadFailure) {
+  base::FilePath extension_path = GetExtensionPath("extension");
+  const ExtensionId extension_id =
+      crx_file::id_util::GenerateIdForPath(extension_path);
+  ExtensionPrefs::Get(browser_context())
+      ->SetExtensionDisabled(extension_id, disable_reason::DISABLE_RELOAD);
+
+  ShellExtensionLoader loader(browser_context());
+  const Extension* extension = loader.LoadExtension(extension_path);
+  ExpectEnabled(extension);
+}
+
+// Tests that the extension is not added if it is disabled in ExtensionPrefs
+// for reasons beyond reloading.
+TEST_F(ShellExtensionLoaderTest, LoadDisabledExtension) {
+  base::FilePath extension_path = GetExtensionPath("extension");
+  const ExtensionId extension_id =
+      crx_file::id_util::GenerateIdForPath(extension_path);
+  ExtensionPrefs::Get(browser_context())
+      ->SetExtensionDisabled(
+          extension_id,
+          disable_reason::DISABLE_RELOAD | disable_reason::DISABLE_USER_ACTION);
+
+  ShellExtensionLoader loader(browser_context());
+  const Extension* extension = loader.LoadExtension(extension_path);
+  ExpectDisabled(extension);
+}
+
+}  // namespace extensions
diff --git a/extensions/shell/browser/shell_extension_system.cc b/extensions/shell/browser/shell_extension_system.cc
index 9d39b66..3bdb3b3 100644
--- a/extensions/shell/browser/shell_extension_system.cc
+++ b/extensions/shell/browser/shell_extension_system.cc
@@ -4,6 +4,7 @@
 
 #include "extensions/shell/browser/shell_extension_system.h"
 
+#include <memory>
 #include <string>
 
 #include "apps/launcher.h"
@@ -15,18 +16,18 @@
 #include "content/public/browser/notification_details.h"
 #include "content/public/browser/notification_service.h"
 #include "content/public/browser/notification_source.h"
-#include "extensions/browser/extension_prefs.h"
+#include "extensions/browser/api/app_runtime/app_runtime_api.h"
 #include "extensions/browser/extension_registry.h"
 #include "extensions/browser/info_map.h"
 #include "extensions/browser/notification_types.h"
 #include "extensions/browser/null_app_sorting.h"
 #include "extensions/browser/quota_service.h"
-#include "extensions/browser/renderer_startup_helper.h"
 #include "extensions/browser/runtime_data.h"
 #include "extensions/browser/service_worker_manager.h"
 #include "extensions/browser/value_store/value_store_factory_impl.h"
 #include "extensions/common/constants.h"
 #include "extensions/common/file_util.h"
+#include "extensions/shell/browser/shell_extension_loader.h"
 
 using content::BrowserContext;
 using content::BrowserThread;
@@ -38,54 +39,11 @@
       store_factory_(new ValueStoreFactoryImpl(browser_context->GetPath())),
       weak_factory_(this) {}
 
-ShellExtensionSystem::~ShellExtensionSystem() {
-}
+ShellExtensionSystem::~ShellExtensionSystem() = default;
 
 const Extension* ShellExtensionSystem::LoadExtension(
     const base::FilePath& extension_dir) {
-  // app_shell only supports unpacked extensions.
-  // NOTE: If you add packed extension support consider removing the flag
-  // FOLLOW_SYMLINKS_ANYWHERE below. Packed extensions should not have symlinks.
-  CHECK(base::DirectoryExists(extension_dir)) << extension_dir.AsUTF8Unsafe();
-  int load_flags = Extension::FOLLOW_SYMLINKS_ANYWHERE;
-  std::string load_error;
-  scoped_refptr<Extension> extension = file_util::LoadExtension(
-      extension_dir, Manifest::COMMAND_LINE, load_flags, &load_error);
-  if (!extension.get()) {
-    LOG(ERROR) << "Loading extension at " << extension_dir.value()
-               << " failed with: " << load_error;
-    return nullptr;
-  }
-
-  // Log warnings.
-  if (extension->install_warnings().size()) {
-    LOG(WARNING) << "Warnings loading extension at " << extension_dir.value()
-                 << ":";
-    for (const auto& warning : extension->install_warnings())
-      LOG(WARNING) << warning.message;
-  }
-
-  // TODO(jamescook): We may want to do some of these things here:
-  // * Create a PermissionsUpdater.
-  // * Call PermissionsUpdater::GrantActivePermissions().
-  // * Call ExtensionService::SatisfyImports().
-  // * Call ExtensionPrefs::OnExtensionInstalled().
-  // * Call ExtensionRegistryObserver::OnExtensionWillbeInstalled().
-
-  ExtensionRegistry::Get(browser_context_)->AddEnabled(extension.get());
-
-  RegisterExtensionWithRequestContexts(
-      extension.get(),
-      base::Bind(
-          &ShellExtensionSystem::OnExtensionRegisteredWithRequestContexts,
-          weak_factory_.GetWeakPtr(), extension));
-
-  RendererStartupHelperFactory::GetForBrowserContext(browser_context_)
-      ->OnExtensionLoaded(*extension);
-
-  ExtensionRegistry::Get(browser_context_)->TriggerOnLoaded(extension.get());
-
-  return extension.get();
+  return extension_loader_->LoadExtension(extension_dir);
 }
 
 const Extension* ShellExtensionSystem::LoadApp(const base::FilePath& app_dir) {
@@ -113,14 +71,17 @@
 }
 
 void ShellExtensionSystem::Shutdown() {
+  extension_loader_.reset();
 }
 
 void ShellExtensionSystem::InitForRegularProfile(bool extensions_enabled) {
-  service_worker_manager_.reset(new ServiceWorkerManager(browser_context_));
-  runtime_data_.reset(
-      new RuntimeData(ExtensionRegistry::Get(browser_context_)));
-  quota_service_.reset(new QuotaService);
-  app_sorting_.reset(new NullAppSorting);
+  service_worker_manager_ =
+      std::make_unique<ServiceWorkerManager>(browser_context_);
+  runtime_data_ =
+      std::make_unique<RuntimeData>(ExtensionRegistry::Get(browser_context_));
+  quota_service_ = std::make_unique<QuotaService>();
+  app_sorting_ = std::make_unique<NullAppSorting>();
+  extension_loader_ = std::make_unique<ShellExtensionLoader>(browser_context_);
 }
 
 void ShellExtensionSystem::InitForIncognitoProfile() {
diff --git a/extensions/shell/browser/shell_extension_system.h b/extensions/shell/browser/shell_extension_system.h
index ebb568a..b25e58b 100644
--- a/extensions/shell/browser/shell_extension_system.h
+++ b/extensions/shell/browser/shell_extension_system.h
@@ -5,10 +5,12 @@
 #ifndef EXTENSIONS_SHELL_BROWSER_SHELL_EXTENSION_SYSTEM_H_
 #define EXTENSIONS_SHELL_BROWSER_SHELL_EXTENSION_SYSTEM_H_
 
+#include <memory>
 #include <vector>
 
 #include "base/compiler_specific.h"
 #include "base/macros.h"
+#include "base/memory/ref_counted.h"
 #include "base/memory/weak_ptr.h"
 #include "extensions/browser/extension_system.h"
 #include "extensions/common/one_shot_event.h"
@@ -23,6 +25,7 @@
 
 namespace extensions {
 
+class ShellExtensionLoader;
 class ValueStoreFactory;
 
 // A simplified version of ExtensionSystem for app_shell. Allows
@@ -47,7 +50,7 @@
   void FinishInitialization();
 
   // Launch the app with id |extension_id|.
-  void LaunchApp(const std::string& extension_id);
+  void LaunchApp(const ExtensionId& extension_id);
 
   // KeyedService implementation:
   void Shutdown() override;
@@ -96,6 +99,8 @@
   std::unique_ptr<QuotaService> quota_service_;
   std::unique_ptr<AppSorting> app_sorting_;
 
+  std::unique_ptr<ShellExtensionLoader> extension_loader_;
+
   scoped_refptr<ValueStoreFactory> store_factory_;
 
   // Signaled when the extension system has completed its startup tasks.
diff --git a/google_apis/gcm/engine/connection_factory_impl.cc b/google_apis/gcm/engine/connection_factory_impl.cc
index b975a21..cfffcb20 100644
--- a/google_apis/gcm/engine/connection_factory_impl.cc
+++ b/google_apis/gcm/engine/connection_factory_impl.cc
@@ -583,31 +583,20 @@
         proxy_info_.proxy_server().host_port_pair());
   }
 
-  int status = gcm_network_session_->proxy_resolution_service()
-                                   ->ReconsiderProxyAfterError(
-      GetCurrentEndpoint(), std::string(), error, &proxy_info_,
-      base::Bind(&ConnectionFactoryImpl::OnProxyResolveDone,
-                 weak_ptr_factory_.GetWeakPtr()),
-      &proxy_resolve_request_, NULL, net_log_);
-  if (status == net::OK || status == net::ERR_IO_PENDING) {
-    CloseSocket();
-  } else {
-    // If ReconsiderProxyAfterError() failed synchronously, it means
-    // there was nothing left to fall-back to, so fail the transaction
+  if (!proxy_info_.Fallback(error, net_log_)) {
+    // There was nothing left to fall-back to, so fail the transaction
     // with the last connection error we got.
-    status = error;
+    return error;
   }
 
-  // If there is new proxy info, post OnProxyResolveDone to retry it. Otherwise,
-  // if there was an error falling back, fail synchronously.
-  if (status == net::OK) {
-    base::ThreadTaskRunnerHandle::Get()->PostTask(
-        FROM_HERE,
-        base::Bind(&ConnectionFactoryImpl::OnProxyResolveDone,
-                   weak_ptr_factory_.GetWeakPtr(), status));
-    status = net::ERR_IO_PENDING;
-  }
-  return status;
+  CloseSocket();
+
+  // If there is new proxy info, post OnProxyResolveDone to retry it.
+  base::ThreadTaskRunnerHandle::Get()->PostTask(
+      FROM_HERE, base::Bind(&ConnectionFactoryImpl::OnProxyResolveDone,
+                            weak_ptr_factory_.GetWeakPtr(), net::OK));
+
+  return net::ERR_IO_PENDING;
 }
 
 void ConnectionFactoryImpl::ReportSuccessfulProxyConnection() {
diff --git a/gpu/BUILD.gn b/gpu/BUILD.gn
index aac12b3..87f7d15 100644
--- a/gpu/BUILD.gn
+++ b/gpu/BUILD.gn
@@ -274,9 +274,12 @@
     "command_buffer/client/fenced_allocator_test.cc",
     "command_buffer/client/gles2_implementation_unittest.cc",
     "command_buffer/client/mapped_memory_unittest.cc",
+    "command_buffer/client/mock_transfer_buffer.cc",
+    "command_buffer/client/mock_transfer_buffer.h",
     "command_buffer/client/program_info_manager_unittest.cc",
     "command_buffer/client/query_tracker_unittest.cc",
     "command_buffer/client/raster_implementation_gles_unittest.cc",
+    "command_buffer/client/raster_implementation_unittest.cc",
     "command_buffer/client/ring_buffer_test.cc",
     "command_buffer/client/transfer_buffer_unittest.cc",
     "command_buffer/client/vertex_array_object_manager_unittest.cc",
diff --git a/gpu/command_buffer/build_raster_cmd_buffer.py b/gpu/command_buffer/build_raster_cmd_buffer.py
index bee9ce17..cd9d64c 100755
--- a/gpu/command_buffer/build_raster_cmd_buffer.py
+++ b/gpu/command_buffer/build_raster_cmd_buffer.py
@@ -919,8 +919,8 @@
     "gpu/command_buffer/client/raster_implementation_autogen.h")
   gen.WriteGLES2Implementation(
     "gpu/command_buffer/client/raster_implementation_impl_autogen.h")
-  # gen.WriteGLES2ImplementationUnitTests(
-  #   "gpu/command_buffer/client/raster_implementation_unittest_autogen.h")
+  gen.WriteGLES2ImplementationUnitTests(
+    "gpu/command_buffer/client/raster_implementation_unittest_autogen.h")
   # gen.WriteGLES2TraceImplementationHeader(
   #     "gpu/command_buffer/client/raster_trace_implementation_autogen.h")
   # gen.WriteGLES2TraceImplementation(
diff --git a/gpu/command_buffer/client/gles2_implementation.h b/gpu/command_buffer/client/gles2_implementation.h
index 66ff9429f..bcd3dd25 100644
--- a/gpu/command_buffer/client/gles2_implementation.h
+++ b/gpu/command_buffer/client/gles2_implementation.h
@@ -72,9 +72,6 @@
     ShaderPrecisionMap shader_precisions;
   };
 
-  // The bucket used for results. Public for testing only.
-  static const uint32_t kResultBucketId = 1;
-
   // GL names for the buffers used to emulate client side buffers.
   static const GLuint kClientSideArrayId = 0xFEDCBA98u;
   static const GLuint kClientSideElementArrayId = 0xFEDCBA99u;
diff --git a/gpu/command_buffer/client/gles2_implementation_unittest.cc b/gpu/command_buffer/client/gles2_implementation_unittest.cc
index ac17565..c25f145 100644
--- a/gpu/command_buffer/client/gles2_implementation_unittest.cc
+++ b/gpu/command_buffer/client/gles2_implementation_unittest.cc
@@ -19,11 +19,11 @@
 #include "base/compiler_specific.h"
 #include "gpu/command_buffer/client/client_test_helper.h"
 #include "gpu/command_buffer/client/gles2_cmd_helper.h"
+#include "gpu/command_buffer/client/mock_transfer_buffer.h"
 #include "gpu/command_buffer/client/program_info_manager.h"
 #include "gpu/command_buffer/client/query_tracker.h"
 #include "gpu/command_buffer/client/ring_buffer.h"
 #include "gpu/command_buffer/client/shared_memory_limits.h"
-#include "gpu/command_buffer/client/transfer_buffer.h"
 #include "gpu/command_buffer/common/command_buffer.h"
 #include "gpu/command_buffer/common/sync_token.h"
 #include "testing/gmock/include/gmock/gmock.h"
@@ -95,254 +95,6 @@
 };
 #pragma pack(pop)
 
-class MockTransferBuffer : public TransferBufferInterface {
- public:
-  struct ExpectedMemoryInfo {
-    uint32_t offset;
-    int32_t id;
-    uint8_t* ptr;
-  };
-
-  MockTransferBuffer(
-      CommandBuffer* command_buffer,
-      unsigned int size,
-      unsigned int result_size,
-      unsigned int alignment,
-      bool initialize_fail)
-      : command_buffer_(command_buffer),
-        size_(size),
-        result_size_(result_size),
-        alignment_(alignment),
-        actual_buffer_index_(0),
-        expected_buffer_index_(0),
-        last_alloc_(NULL),
-        expected_offset_(result_size),
-        actual_offset_(result_size),
-        initialize_fail_(initialize_fail) {
-    // We have to allocate the buffers here because
-    // we need to know their address before GLES2Implementation::Initialize
-    // is called.
-    for (int ii = 0; ii < kNumBuffers; ++ii) {
-      buffers_[ii] = command_buffer_->CreateTransferBuffer(
-          size_ + ii * alignment_,
-          &buffer_ids_[ii]);
-      EXPECT_NE(-1, buffer_ids_[ii]);
-    }
-  }
-
-  ~MockTransferBuffer() override = default;
-
-  base::SharedMemoryHandle shared_memory_handle() const override;
-  bool Initialize(unsigned int starting_buffer_size,
-                  unsigned int result_size,
-                  unsigned int /* min_buffer_size */,
-                  unsigned int /* max_buffer_size */,
-                  unsigned int alignment,
-                  unsigned int size_to_flush) override;
-  int GetShmId() override;
-  void* GetResultBuffer() override;
-  int GetResultOffset() override;
-  void Free() override;
-  bool HaveBuffer() const override;
-  void* AllocUpTo(unsigned int size, unsigned int* size_allocated) override;
-  void* Alloc(unsigned int size) override;
-  RingBuffer::Offset GetOffset(void* pointer) const override;
-  void DiscardBlock(void* p) override;
-  void FreePendingToken(void* p, unsigned int /* token */) override;
-  unsigned int GetSize() const override;
-  unsigned int GetFreeSize() const override;
-  unsigned int GetFragmentedFreeSize() const override;
-  void ShrinkLastBlock(unsigned int new_size) override;
-
-  size_t MaxTransferBufferSize() {
-    return size_ - result_size_;
-  }
-
-  unsigned int RoundToAlignment(unsigned int size) {
-    return (size + alignment_ - 1) & ~(alignment_ - 1);
-  }
-
-  bool InSync() {
-    return expected_buffer_index_ == actual_buffer_index_ &&
-           expected_offset_ == actual_offset_;
-  }
-
-  ExpectedMemoryInfo GetExpectedMemory(size_t size) {
-    ExpectedMemoryInfo mem;
-    mem.offset = AllocateExpectedTransferBuffer(size);
-    mem.id = GetExpectedTransferBufferId();
-    mem.ptr = static_cast<uint8_t*>(
-        GetExpectedTransferAddressFromOffset(mem.offset, size));
-    return mem;
-  }
-
-  ExpectedMemoryInfo GetExpectedResultMemory(size_t size) {
-    ExpectedMemoryInfo mem;
-    mem.offset = GetExpectedResultBufferOffset();
-    mem.id = GetExpectedResultBufferId();
-    mem.ptr = static_cast<uint8_t*>(
-        GetExpectedTransferAddressFromOffset(mem.offset, size));
-    return mem;
-  }
-
- private:
-  static const int kNumBuffers = 2;
-
-  uint8_t* actual_buffer() const {
-    return static_cast<uint8_t*>(buffers_[actual_buffer_index_]->memory());
-  }
-
-  uint8_t* expected_buffer() const {
-    return static_cast<uint8_t*>(buffers_[expected_buffer_index_]->memory());
-  }
-
-  uint32_t AllocateExpectedTransferBuffer(size_t size) {
-    EXPECT_LE(size, MaxTransferBufferSize());
-
-    // Toggle which buffer we get each time to simulate the buffer being
-    // reallocated.
-    expected_buffer_index_ = (expected_buffer_index_ + 1) % kNumBuffers;
-
-    if (expected_offset_ + size > size_) {
-      expected_offset_ = result_size_;
-    }
-    uint32_t offset = expected_offset_;
-    expected_offset_ += RoundToAlignment(size);
-
-    // Make sure each buffer has a different offset.
-    return offset + expected_buffer_index_ * alignment_;
-  }
-
-  void* GetExpectedTransferAddressFromOffset(uint32_t offset, size_t size) {
-    EXPECT_GE(offset, expected_buffer_index_ * alignment_);
-    EXPECT_LE(offset + size, size_ + expected_buffer_index_ * alignment_);
-    return expected_buffer() + offset;
-  }
-
-  int GetExpectedResultBufferId() {
-    return buffer_ids_[expected_buffer_index_];
-  }
-
-  uint32_t GetExpectedResultBufferOffset() {
-    return expected_buffer_index_ * alignment_;
-  }
-
-  int GetExpectedTransferBufferId() {
-    return buffer_ids_[expected_buffer_index_];
-  }
-
-  CommandBuffer* command_buffer_;
-  size_t size_;
-  size_t result_size_;
-  uint32_t alignment_;
-  int buffer_ids_[kNumBuffers];
-  scoped_refptr<Buffer> buffers_[kNumBuffers];
-  int actual_buffer_index_;
-  int expected_buffer_index_;
-  void* last_alloc_;
-  uint32_t expected_offset_;
-  uint32_t actual_offset_;
-  bool initialize_fail_;
-
-  DISALLOW_COPY_AND_ASSIGN(MockTransferBuffer);
-};
-
-base::SharedMemoryHandle MockTransferBuffer::shared_memory_handle() const {
-  return base::SharedMemoryHandle();
-}
-
-bool MockTransferBuffer::Initialize(
-    unsigned int starting_buffer_size,
-    unsigned int result_size,
-    unsigned int /* min_buffer_size */,
-    unsigned int /* max_buffer_size */,
-    unsigned int alignment,
-    unsigned int /* size_to_flush */) {
-  // Just check they match.
-  return size_ == starting_buffer_size &&
-         result_size_ == result_size &&
-         alignment_ == alignment && !initialize_fail_;
-};
-
-int MockTransferBuffer::GetShmId() {
-  return buffer_ids_[actual_buffer_index_];
-}
-
-void* MockTransferBuffer::GetResultBuffer() {
-  return actual_buffer() + actual_buffer_index_ * alignment_;
-}
-
-int MockTransferBuffer::GetResultOffset() {
-  return actual_buffer_index_ * alignment_;
-}
-
-void MockTransferBuffer::Free() {
-  NOTREACHED();
-}
-
-bool MockTransferBuffer::HaveBuffer() const {
-  return true;
-}
-
-void* MockTransferBuffer::AllocUpTo(
-    unsigned int size, unsigned int* size_allocated) {
-  EXPECT_TRUE(size_allocated != NULL);
-  EXPECT_TRUE(last_alloc_ == NULL);
-
-  // Toggle which buffer we get each time to simulate the buffer being
-  // reallocated.
-  actual_buffer_index_ = (actual_buffer_index_ + 1) % kNumBuffers;
-
-  size = std::min(static_cast<size_t>(size), MaxTransferBufferSize());
-  if (actual_offset_ + size > size_) {
-    actual_offset_ = result_size_;
-  }
-  uint32_t offset = actual_offset_;
-  actual_offset_ += RoundToAlignment(size);
-  *size_allocated = size;
-
-  // Make sure each buffer has a different offset.
-  last_alloc_ = actual_buffer() + offset + actual_buffer_index_ * alignment_;
-  return last_alloc_;
-}
-
-void* MockTransferBuffer::Alloc(unsigned int size) {
-  EXPECT_LE(size, MaxTransferBufferSize());
-  unsigned int temp = 0;
-  void* p = AllocUpTo(size, &temp);
-  EXPECT_EQ(temp, size);
-  return p;
-}
-
-RingBuffer::Offset MockTransferBuffer::GetOffset(void* pointer) const {
-  // Make sure each buffer has a different offset.
-  return static_cast<uint8_t*>(pointer) - actual_buffer();
-}
-
-void MockTransferBuffer::DiscardBlock(void* p) {
-  EXPECT_EQ(last_alloc_, p);
-  last_alloc_ = NULL;
-}
-
-void MockTransferBuffer::FreePendingToken(void* p, unsigned int /* token */) {
-  EXPECT_EQ(last_alloc_, p);
-  last_alloc_ = NULL;
-}
-
-unsigned int MockTransferBuffer::GetSize() const {
-  return 0;
-}
-
-unsigned int MockTransferBuffer::GetFreeSize() const {
-  return 0;
-}
-
-unsigned int MockTransferBuffer::GetFragmentedFreeSize() const {
-  return 0;
-}
-
-void MockTransferBuffer::ShrinkLastBlock(unsigned int new_size) {}
-
 // API wrapper for Buffers.
 class GenBuffersAPI {
  public:
@@ -4545,7 +4297,7 @@
 TEST_F(GLES2ImplementationTest, ReportLoss) {
   GpuControlClient* gl_as_client = gl_;
   int lost_count = 0;
-  gl_->SetLostContextCallback(base::Bind(&CountCallback, &lost_count));
+  gl_->SetLostContextCallback(base::BindOnce(&CountCallback, &lost_count));
   EXPECT_EQ(0, lost_count);
 
   EXPECT_EQ(static_cast<GLenum>(GL_NO_ERROR), gl_->GetGraphicsResetStatusKHR());
@@ -4559,7 +4311,7 @@
 TEST_F(GLES2ImplementationTest, ReportLossReentrant) {
   GpuControlClient* gl_as_client = gl_;
   int lost_count = 0;
-  gl_->SetLostContextCallback(base::Bind(&CountCallback, &lost_count));
+  gl_->SetLostContextCallback(base::BindOnce(&CountCallback, &lost_count));
   EXPECT_EQ(0, lost_count);
 
   EXPECT_EQ(static_cast<GLenum>(GL_NO_ERROR), gl_->GetGraphicsResetStatusKHR());
diff --git a/gpu/command_buffer/client/implementation_base.h b/gpu/command_buffer/client/implementation_base.h
index 444ebc5..9257810a 100644
--- a/gpu/command_buffer/client/implementation_base.h
+++ b/gpu/command_buffer/client/implementation_base.h
@@ -57,6 +57,9 @@
   // Alignment of allocations.
   static const unsigned int kAlignment = 16;
 
+  // The bucket used for results. Public for testing only.
+  static const uint32_t kResultBucketId = 1;
+
   ImplementationBase(CommandBufferHelper* helper,
                      TransferBufferInterface* transfer_buffer,
                      GpuControl* gpu_control);
diff --git a/gpu/command_buffer/client/mock_transfer_buffer.cc b/gpu/command_buffer/client/mock_transfer_buffer.cc
new file mode 100644
index 0000000..f371c25
--- /dev/null
+++ b/gpu/command_buffer/client/mock_transfer_buffer.cc
@@ -0,0 +1,202 @@
+// 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/client/mock_transfer_buffer.h"
+
+#include "gpu/command_buffer/common/command_buffer.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace gpu {
+
+MockTransferBuffer::MockTransferBuffer(CommandBuffer* command_buffer,
+                                       unsigned int size,
+                                       unsigned int result_size,
+                                       unsigned int alignment,
+                                       bool initialize_fail)
+    : command_buffer_(command_buffer),
+      size_(size),
+      result_size_(result_size),
+      alignment_(alignment),
+      actual_buffer_index_(0),
+      expected_buffer_index_(0),
+      last_alloc_(NULL),
+      expected_offset_(result_size),
+      actual_offset_(result_size),
+      initialize_fail_(initialize_fail) {
+  // We have to allocate the buffers here because
+  // we need to know their address before
+  // {Raster,GLES2}Implementation::Initialize is called.
+  for (int ii = 0; ii < kNumBuffers; ++ii) {
+    buffers_[ii] = command_buffer_->CreateTransferBuffer(
+        size_ + ii * alignment_, &buffer_ids_[ii]);
+    EXPECT_NE(-1, buffer_ids_[ii]);
+  }
+}
+
+MockTransferBuffer::~MockTransferBuffer() = default;
+
+base::SharedMemoryHandle MockTransferBuffer::shared_memory_handle() const {
+  return base::SharedMemoryHandle();
+}
+
+bool MockTransferBuffer::Initialize(unsigned int starting_buffer_size,
+                                    unsigned int result_size,
+                                    unsigned int /* min_buffer_size */,
+                                    unsigned int /* max_buffer_size */,
+                                    unsigned int alignment,
+                                    unsigned int /* size_to_flush */) {
+  // Just check they match.
+  return size_ == starting_buffer_size && result_size_ == result_size &&
+         alignment_ == alignment && !initialize_fail_;
+};
+
+int MockTransferBuffer::GetShmId() {
+  return buffer_ids_[actual_buffer_index_];
+}
+
+void* MockTransferBuffer::GetResultBuffer() {
+  return actual_buffer() + actual_buffer_index_ * alignment_;
+}
+
+int MockTransferBuffer::GetResultOffset() {
+  return actual_buffer_index_ * alignment_;
+}
+
+void MockTransferBuffer::Free() {
+  NOTREACHED();
+}
+
+bool MockTransferBuffer::HaveBuffer() const {
+  return true;
+}
+
+void* MockTransferBuffer::AllocUpTo(unsigned int size,
+                                    unsigned int* size_allocated) {
+  EXPECT_TRUE(size_allocated != nullptr);
+  EXPECT_TRUE(last_alloc_ == nullptr);
+
+  // Toggle which buffer we get each time to simulate the buffer being
+  // reallocated.
+  actual_buffer_index_ = (actual_buffer_index_ + 1) % kNumBuffers;
+
+  size = std::min(static_cast<size_t>(size), MaxTransferBufferSize());
+  if (actual_offset_ + size > size_) {
+    actual_offset_ = result_size_;
+  }
+  uint32_t offset = actual_offset_;
+  actual_offset_ += RoundToAlignment(size);
+  *size_allocated = size;
+
+  // Make sure each buffer has a different offset.
+  last_alloc_ = actual_buffer() + offset + actual_buffer_index_ * alignment_;
+  return last_alloc_;
+}
+
+void* MockTransferBuffer::Alloc(unsigned int size) {
+  EXPECT_LE(size, MaxTransferBufferSize());
+  unsigned int temp = 0;
+  void* p = AllocUpTo(size, &temp);
+  EXPECT_EQ(temp, size);
+  return p;
+}
+
+RingBuffer::Offset MockTransferBuffer::GetOffset(void* pointer) const {
+  // Make sure each buffer has a different offset.
+  return static_cast<uint8_t*>(pointer) - actual_buffer();
+}
+
+void MockTransferBuffer::DiscardBlock(void* p) {
+  EXPECT_EQ(last_alloc_, p);
+  last_alloc_ = nullptr;
+}
+
+void MockTransferBuffer::FreePendingToken(void* p, unsigned int /* token */) {
+  EXPECT_EQ(last_alloc_, p);
+  last_alloc_ = nullptr;
+}
+
+unsigned int MockTransferBuffer::GetSize() const {
+  return 0;
+}
+
+unsigned int MockTransferBuffer::GetFreeSize() const {
+  return 0;
+}
+
+unsigned int MockTransferBuffer::GetFragmentedFreeSize() const {
+  return 0;
+}
+
+void MockTransferBuffer::ShrinkLastBlock(unsigned int new_size) {}
+
+size_t MockTransferBuffer::MaxTransferBufferSize() {
+  return size_ - result_size_;
+}
+
+unsigned int MockTransferBuffer::RoundToAlignment(unsigned int size) {
+  return (size + alignment_ - 1) & ~(alignment_ - 1);
+}
+
+bool MockTransferBuffer::InSync() {
+  return expected_buffer_index_ == actual_buffer_index_ &&
+         expected_offset_ == actual_offset_;
+}
+
+MockTransferBuffer::ExpectedMemoryInfo MockTransferBuffer::GetExpectedMemory(
+    size_t size) {
+  ExpectedMemoryInfo mem;
+  mem.offset = AllocateExpectedTransferBuffer(size);
+  mem.id = GetExpectedTransferBufferId();
+  mem.ptr = static_cast<uint8_t*>(
+      GetExpectedTransferAddressFromOffset(mem.offset, size));
+  return mem;
+}
+
+MockTransferBuffer::ExpectedMemoryInfo
+MockTransferBuffer::GetExpectedResultMemory(size_t size) {
+  ExpectedMemoryInfo mem;
+  mem.offset = GetExpectedResultBufferOffset();
+  mem.id = GetExpectedResultBufferId();
+  mem.ptr = static_cast<uint8_t*>(
+      GetExpectedTransferAddressFromOffset(mem.offset, size));
+  return mem;
+}
+
+uint32_t MockTransferBuffer::AllocateExpectedTransferBuffer(size_t size) {
+  EXPECT_LE(size, MaxTransferBufferSize());
+
+  // Toggle which buffer we get each time to simulate the buffer being
+  // reallocated.
+  expected_buffer_index_ = (expected_buffer_index_ + 1) % kNumBuffers;
+
+  if (expected_offset_ + size > size_) {
+    expected_offset_ = result_size_;
+  }
+  uint32_t offset = expected_offset_;
+  expected_offset_ += RoundToAlignment(size);
+
+  // Make sure each buffer has a different offset.
+  return offset + expected_buffer_index_ * alignment_;
+}
+
+void* MockTransferBuffer::GetExpectedTransferAddressFromOffset(uint32_t offset,
+                                                               size_t size) {
+  EXPECT_GE(offset, expected_buffer_index_ * alignment_);
+  EXPECT_LE(offset + size, size_ + expected_buffer_index_ * alignment_);
+  return expected_buffer() + offset;
+}
+
+int MockTransferBuffer::GetExpectedResultBufferId() {
+  return buffer_ids_[expected_buffer_index_];
+}
+
+uint32_t MockTransferBuffer::GetExpectedResultBufferOffset() {
+  return expected_buffer_index_ * alignment_;
+}
+
+int MockTransferBuffer::GetExpectedTransferBufferId() {
+  return buffer_ids_[expected_buffer_index_];
+}
+
+}  // namespace gpu
diff --git a/gpu/command_buffer/client/mock_transfer_buffer.h b/gpu/command_buffer/client/mock_transfer_buffer.h
new file mode 100644
index 0000000..a07e00f2
--- /dev/null
+++ b/gpu/command_buffer/client/mock_transfer_buffer.h
@@ -0,0 +1,96 @@
+// 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_CLIENT_MOCK_TRANSFER_BUFFER_H_
+#define GPU_COMMAND_BUFFER_CLIENT_MOCK_TRANSFER_BUFFER_H_
+
+#include "base/macros.h"
+#include "base/memory/shared_memory_handle.h"
+#include "gpu/command_buffer/client/ring_buffer.h"
+#include "gpu/command_buffer/client/transfer_buffer.h"
+
+namespace gpu {
+
+class CommandBuffer;
+
+class MockTransferBuffer : public TransferBufferInterface {
+ public:
+  struct ExpectedMemoryInfo {
+    uint32_t offset;
+    int32_t id;
+    uint8_t* ptr;
+  };
+
+  MockTransferBuffer(CommandBuffer* command_buffer,
+                     unsigned int size,
+                     unsigned int result_size,
+                     unsigned int alignment,
+                     bool initialize_fail);
+
+  ~MockTransferBuffer() override;
+
+  base::SharedMemoryHandle shared_memory_handle() const override;
+  bool Initialize(unsigned int starting_buffer_size,
+                  unsigned int result_size,
+                  unsigned int /* min_buffer_size */,
+                  unsigned int /* max_buffer_size */,
+                  unsigned int alignment,
+                  unsigned int size_to_flush) override;
+  int GetShmId() override;
+  void* GetResultBuffer() override;
+  int GetResultOffset() override;
+  void Free() override;
+  bool HaveBuffer() const override;
+  void* AllocUpTo(unsigned int size, unsigned int* size_allocated) override;
+  void* Alloc(unsigned int size) override;
+  RingBuffer::Offset GetOffset(void* pointer) const override;
+  void DiscardBlock(void* p) override;
+  void FreePendingToken(void* p, unsigned int /* token */) override;
+  unsigned int GetSize() const override;
+  unsigned int GetFreeSize() const override;
+  unsigned int GetFragmentedFreeSize() const override;
+  void ShrinkLastBlock(unsigned int new_size) override;
+
+  size_t MaxTransferBufferSize();
+  unsigned int RoundToAlignment(unsigned int size);
+  bool InSync();
+  ExpectedMemoryInfo GetExpectedMemory(size_t size);
+  ExpectedMemoryInfo GetExpectedResultMemory(size_t size);
+
+ private:
+  static const int kNumBuffers = 2;
+
+  uint8_t* actual_buffer() const {
+    return static_cast<uint8_t*>(buffers_[actual_buffer_index_]->memory());
+  }
+
+  uint8_t* expected_buffer() const {
+    return static_cast<uint8_t*>(buffers_[expected_buffer_index_]->memory());
+  }
+
+  uint32_t AllocateExpectedTransferBuffer(size_t size);
+  void* GetExpectedTransferAddressFromOffset(uint32_t offset, size_t size);
+  int GetExpectedResultBufferId();
+  uint32_t GetExpectedResultBufferOffset();
+  int GetExpectedTransferBufferId();
+
+  CommandBuffer* command_buffer_;
+  size_t size_;
+  size_t result_size_;
+  uint32_t alignment_;
+  int buffer_ids_[kNumBuffers];
+  scoped_refptr<Buffer> buffers_[kNumBuffers];
+  int actual_buffer_index_;
+  int expected_buffer_index_;
+  void* last_alloc_;
+  uint32_t expected_offset_;
+  uint32_t actual_offset_;
+  bool initialize_fail_;
+
+  DISALLOW_COPY_AND_ASSIGN(MockTransferBuffer);
+};
+
+}  // namespace gpu
+
+#endif  // GPU_COMMAND_BUFFER_CLIENT_MOCK_TRANSFER_BUFFER_H_
diff --git a/gpu/command_buffer/client/raster_implementation.cc b/gpu/command_buffer/client/raster_implementation.cc
index d70ad697..92be4cad 100644
--- a/gpu/command_buffer/client/raster_implementation.cc
+++ b/gpu/command_buffer/client/raster_implementation.cc
@@ -93,6 +93,7 @@
     GpuControl* gpu_control)
     : ImplementationBase(helper, transfer_buffer, gpu_control),
       helper_(helper),
+      active_texture_unit_(0),
       error_bits_(0),
       lose_context_when_out_of_memory_(lose_context_when_out_of_memory),
       use_count_(0),
@@ -395,10 +396,15 @@
 
 bool RasterImplementation::GetIntegervHelper(GLenum pname, GLint* params) {
   switch (pname) {
+    case GL_ACTIVE_TEXTURE:
+      *params = active_texture_unit_ + GL_TEXTURE0;
+      return true;
     case GL_MAX_TEXTURE_SIZE:
       *params = capabilities_.max_texture_size;
       return true;
-
+    case GL_TEXTURE_BINDING_2D:
+      *params = texture_units_[active_texture_unit_].bound_texture_2d;
+      return true;
     default:
       return false;
   }
@@ -465,12 +471,16 @@
   FlushHelper();
 }
 
-void RasterImplementation::ShallowFlushCHROMIUM() {
+void RasterImplementation::IssueShallowFlush() {
   GPU_CLIENT_SINGLE_THREAD_CHECK();
   GPU_CLIENT_LOG("[" << GetLogPrefix() << "] glShallowFlushCHROMIUM()");
   FlushHelper();
 }
 
+void RasterImplementation::ShallowFlushCHROMIUM() {
+  IssueShallowFlush();
+}
+
 void RasterImplementation::FlushHelper() {
   // Flush our command buffer
   // (tell the service to execute up to the flush cmd.)
@@ -834,12 +844,12 @@
 }
 
 bool RasterImplementation::LockDiscardableTextureCHROMIUM(GLuint texture_id) {
-  if (discardable_texture_manager_.TextureIsValid(texture_id)) {
+  if (!discardable_texture_manager_.TextureIsValid(texture_id)) {
     SetGLError(GL_INVALID_VALUE, "glLockDiscardableTextureCHROMIUM",
                "Texture ID not initialized");
     return false;
   }
-  if (discardable_texture_manager_.LockTexture(texture_id)) {
+  if (!discardable_texture_manager_.LockTexture(texture_id)) {
     // Failure to lock means that this texture has been deleted on the service
     // side. Delete it here as well.
     DeleteTexturesHelper(1, &texture_id);
@@ -895,5 +905,172 @@
 // instead of having to edit some template or the code generator.
 #include "gpu/command_buffer/client/raster_implementation_impl_autogen.h"
 
+void RasterImplementation::GenTextures(GLsizei n, GLuint* textures) {
+  GPU_CLIENT_LOG("[" << GetLogPrefix() << "] glGenTextures(" << n << ", "
+                     << static_cast<const void*>(textures) << ")");
+  if (n < 0) {
+    SetGLError(GL_INVALID_VALUE, "glGenTextures", "n < 0");
+    return;
+  }
+  GPU_CLIENT_SINGLE_THREAD_CHECK();
+  for (int ii = 0; ii < n; ++ii) {
+    textures[ii] = texture_id_allocator_.AllocateID();
+  }
+  // TODO(backer): Send some signal to service side.
+  // helper_->GenTexturesImmediate(n, textures);
+  // if (share_group_->bind_generates_resource())
+  //   helper_->CommandBufferHelper::Flush();
+
+  GPU_CLIENT_LOG_CODE_BLOCK({
+    for (GLsizei i = 0; i < n; ++i) {
+      GPU_CLIENT_LOG("  " << i << ": " << textures[i]);
+    }
+  });
+  CheckGLError();
+}
+
+void RasterImplementation::BindTexture(GLenum target, GLuint texture) {
+  GPU_CLIENT_SINGLE_THREAD_CHECK();
+  GPU_CLIENT_LOG("[" << GetLogPrefix() << "] glBindTexture("
+                     << GLES2Util::GetStringEnum(texture) << ")");
+  DCHECK_EQ(target, static_cast<GLenum>(GL_TEXTURE_2D));
+  if (target != GL_TEXTURE_2D) {
+    return;
+  }
+  TextureUnit& unit = texture_units_[active_texture_unit_];
+  unit.bound_texture_2d = texture;
+  // TODO(backer): Update bound texture on the server side.
+  // helper_->BindTexture(target, texture);
+  texture_id_allocator_.MarkAsUsed(texture);
+}
+
+void RasterImplementation::ActiveTexture(GLenum texture) {
+  GPU_CLIENT_SINGLE_THREAD_CHECK();
+  GPU_CLIENT_LOG("[" << GetLogPrefix() << "] glActiveTexture("
+                     << GLES2Util::GetStringEnum(texture) << ")");
+  GLuint texture_index = texture - GL_TEXTURE0;
+  if (texture_index >=
+      static_cast<GLuint>(capabilities_.max_combined_texture_image_units)) {
+    SetGLErrorInvalidEnum("glActiveTexture", texture, "texture");
+    return;
+  }
+
+  active_texture_unit_ = texture_index;
+  // TODO(backer): Update active texture on the server side.
+  // helper_->ActiveTexture(texture);
+  CheckGLError();
+}
+
+void RasterImplementation::GenerateMipmap(GLenum target) {
+  NOTIMPLEMENTED();
+}
+void RasterImplementation::SetColorSpaceMetadataCHROMIUM(
+    GLuint texture_id,
+    GLColorSpace color_space) {
+  NOTIMPLEMENTED();
+}
+void RasterImplementation::GenMailboxCHROMIUM(GLbyte* mailbox) {
+  NOTIMPLEMENTED();
+}
+void RasterImplementation::ProduceTextureDirectCHROMIUM(GLuint texture,
+                                                        const GLbyte* mailbox) {
+  NOTIMPLEMENTED();
+}
+GLuint RasterImplementation::CreateAndConsumeTextureCHROMIUM(
+    const GLbyte* mailbox) {
+  NOTIMPLEMENTED();
+  return 0;
+}
+void RasterImplementation::BindTexImage2DCHROMIUM(GLenum target,
+                                                  GLint imageId) {
+  NOTIMPLEMENTED();
+}
+void RasterImplementation::ReleaseTexImage2DCHROMIUM(GLenum target,
+                                                     GLint imageId) {
+  NOTIMPLEMENTED();
+}
+void RasterImplementation::TexImage2D(GLenum target,
+                                      GLint level,
+                                      GLint internalformat,
+                                      GLsizei width,
+                                      GLsizei height,
+                                      GLint border,
+                                      GLenum format,
+                                      GLenum type,
+                                      const void* pixels) {
+  NOTIMPLEMENTED();
+}
+void RasterImplementation::TexSubImage2D(GLenum target,
+                                         GLint level,
+                                         GLint xoffset,
+                                         GLint yoffset,
+                                         GLsizei width,
+                                         GLsizei height,
+                                         GLenum format,
+                                         GLenum type,
+                                         const void* pixels) {
+  NOTIMPLEMENTED();
+}
+void RasterImplementation::CompressedTexImage2D(GLenum target,
+                                                GLint level,
+                                                GLenum internalformat,
+                                                GLsizei width,
+                                                GLsizei height,
+                                                GLint border,
+                                                GLsizei imageSize,
+                                                const void* data) {
+  NOTIMPLEMENTED();
+}
+void RasterImplementation::TexStorageForRaster(GLenum target,
+                                               viz::ResourceFormat format,
+                                               GLsizei width,
+                                               GLsizei height,
+                                               RasterTexStorageFlags flags) {
+  NOTIMPLEMENTED();
+}
+void RasterImplementation::CopySubTextureCHROMIUM(
+    GLuint source_id,
+    GLint source_level,
+    GLenum dest_target,
+    GLuint dest_id,
+    GLint dest_level,
+    GLint xoffset,
+    GLint yoffset,
+    GLint x,
+    GLint y,
+    GLsizei width,
+    GLsizei height,
+    GLboolean unpack_flip_y,
+    GLboolean unpack_premultiply_alpha,
+    GLboolean unpack_unmultiply_alpha) {
+  NOTIMPLEMENTED();
+}
+void RasterImplementation::BeginRasterCHROMIUM(
+    GLuint texture_id,
+    GLuint sk_color,
+    GLuint msaa_sample_count,
+    GLboolean can_use_lcd_text,
+    GLboolean use_distance_field_text,
+    GLint pixel_config,
+    const cc::RasterColorSpace& raster_color_space) {
+  NOTIMPLEMENTED();
+}
+void RasterImplementation::RasterCHROMIUM(const cc::DisplayItemList* list,
+                                          cc::ImageProvider* provider,
+                                          const gfx::Size& content_size,
+                                          const gfx::Rect& full_raster_rect,
+                                          const gfx::Rect& playback_rect,
+                                          const gfx::Vector2dF& post_translate,
+                                          GLfloat post_scale,
+                                          bool requires_clear) {
+  NOTIMPLEMENTED();
+}
+void RasterImplementation::BeginGpuRaster() {
+  NOTIMPLEMENTED();
+}
+void RasterImplementation::EndGpuRaster() {
+  NOTIMPLEMENTED();
+}
+
 }  // namespace raster
 }  // namespace gpu
diff --git a/gpu/command_buffer/client/raster_implementation.h b/gpu/command_buffer/client/raster_implementation.h
index f4abfe0..f01843f 100644
--- a/gpu/command_buffer/client/raster_implementation.h
+++ b/gpu/command_buffer/client/raster_implementation.h
@@ -98,6 +98,83 @@
 // this file instead of having to edit some template or the code generator.
 #include "gpu/command_buffer/client/raster_implementation_autogen.h"
 
+  // RasterInterface implementation.
+  void GenTextures(GLsizei n, GLuint* textures) override;
+  void BindTexture(GLenum target, GLuint texture) override;
+  void ActiveTexture(GLenum texture) override;
+  void GenerateMipmap(GLenum target) override;
+  void SetColorSpaceMetadataCHROMIUM(GLuint texture_id,
+                                     GLColorSpace color_space) override;
+  void GenMailboxCHROMIUM(GLbyte* mailbox) override;
+  void ProduceTextureDirectCHROMIUM(GLuint texture,
+                                    const GLbyte* mailbox) override;
+  GLuint CreateAndConsumeTextureCHROMIUM(const GLbyte* mailbox) override;
+  void BindTexImage2DCHROMIUM(GLenum target, GLint imageId) override;
+  void ReleaseTexImage2DCHROMIUM(GLenum target, GLint imageId) override;
+  void TexImage2D(GLenum target,
+                  GLint level,
+                  GLint internalformat,
+                  GLsizei width,
+                  GLsizei height,
+                  GLint border,
+                  GLenum format,
+                  GLenum type,
+                  const void* pixels) override;
+  void TexSubImage2D(GLenum target,
+                     GLint level,
+                     GLint xoffset,
+                     GLint yoffset,
+                     GLsizei width,
+                     GLsizei height,
+                     GLenum format,
+                     GLenum type,
+                     const void* pixels) override;
+  void CompressedTexImage2D(GLenum target,
+                            GLint level,
+                            GLenum internalformat,
+                            GLsizei width,
+                            GLsizei height,
+                            GLint border,
+                            GLsizei imageSize,
+                            const void* data) override;
+  void TexStorageForRaster(GLenum target,
+                           viz::ResourceFormat format,
+                           GLsizei width,
+                           GLsizei height,
+                           RasterTexStorageFlags flags) override;
+  void CopySubTextureCHROMIUM(GLuint source_id,
+                              GLint source_level,
+                              GLenum dest_target,
+                              GLuint dest_id,
+                              GLint dest_level,
+                              GLint xoffset,
+                              GLint yoffset,
+                              GLint x,
+                              GLint y,
+                              GLsizei width,
+                              GLsizei height,
+                              GLboolean unpack_flip_y,
+                              GLboolean unpack_premultiply_alpha,
+                              GLboolean unpack_unmultiply_alpha) override;
+  void BeginRasterCHROMIUM(
+      GLuint texture_id,
+      GLuint sk_color,
+      GLuint msaa_sample_count,
+      GLboolean can_use_lcd_text,
+      GLboolean use_distance_field_text,
+      GLint pixel_config,
+      const cc::RasterColorSpace& raster_color_space) override;
+  void RasterCHROMIUM(const cc::DisplayItemList* list,
+                      cc::ImageProvider* provider,
+                      const gfx::Size& content_size,
+                      const gfx::Rect& full_raster_rect,
+                      const gfx::Rect& playback_rect,
+                      const gfx::Vector2dF& post_translate,
+                      GLfloat post_scale,
+                      bool requires_clear) override;
+  void BeginGpuRaster() override;
+  void EndGpuRaster() override;
+
   // ContextSupport implementation.
   void SetAggressivelyFreeResources(bool aggressively_free_resources) override;
   void Swap() override;
@@ -119,16 +196,14 @@
   bool ThreadsafeDiscardableTextureIsDeletedForTracing(
       uint32_t texture_id) override;
 
-  // TODO(danakj): Move to ContextSupport once ContextProvider doesn't need to
-  // intercept it.
-  void SetLostContextCallback(base::OnceClosure callback);
-
   bool GetQueryObjectValueHelper(const char* function_name,
                                  GLuint id,
                                  GLenum pname,
                                  GLuint64* params);
 
  private:
+  friend class RasterImplementationTest;
+
   using IdNamespaces = gles2::id_namespaces::IdNamespaces;
 
   struct TextureUnit {
@@ -147,6 +222,9 @@
     RasterImplementation* raster_implementation_;
   };
 
+  // ImplementationBase implementation.
+  void IssueShallowFlush() override;
+
   // GpuControlClient implementation.
   void OnGpuControlLostContext() final;
   void OnGpuControlLostContextMaybeReentrant() final;
@@ -209,13 +287,15 @@
   void UnmapRasterCHROMIUM(GLsizeiptr written_size);
 
   RasterCmdHelper* helper_;
-  TransferBufferInterface* transfer_buffer_;
   std::string last_error_;
   gles2::DebugMarkerManager debug_marker_manager_;
   std::string this_in_hex_;
 
   std::unique_ptr<TextureUnit[]> texture_units_;
 
+  // 0 to capabilities_.max_combined_texture_image_units.
+  GLuint active_texture_unit_;
+
   // Current GL error bits.
   uint32_t error_bits_;
 
@@ -230,8 +310,6 @@
   base::Optional<ScopedTransferBufferPtr> raster_mapped_buffer_;
 
   base::RepeatingCallback<void(const char*, int32_t)> error_message_callback_;
-  base::OnceClosure lost_context_callback_;
-  bool lost_context_callback_run_ = false;
 
   int current_trace_stack_;
 
diff --git a/gpu/command_buffer/client/raster_implementation_unittest.cc b/gpu/command_buffer/client/raster_implementation_unittest.cc
new file mode 100644
index 0000000..7e38ec7a
--- /dev/null
+++ b/gpu/command_buffer/client/raster_implementation_unittest.cc
@@ -0,0 +1,1019 @@
+// 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.
+
+// Tests for RasterImplementation.
+
+#include "gpu/command_buffer/client/raster_implementation.h"
+
+#include <GLES2/gl2.h>
+#include <GLES2/gl2extchromium.h>
+
+#include <stddef.h>
+#include <stdint.h>
+
+#include <memory>
+
+#include "base/compiler_specific.h"
+#include "gpu/command_buffer/client/client_test_helper.h"
+#include "gpu/command_buffer/client/mock_transfer_buffer.h"
+#include "gpu/command_buffer/client/query_tracker.h"
+#include "gpu/command_buffer/client/raster_cmd_helper.h"
+#include "gpu/command_buffer/client/ring_buffer.h"
+#include "gpu/command_buffer/client/shared_memory_limits.h"
+#include "gpu/command_buffer/client/transfer_buffer.h"
+#include "gpu/command_buffer/common/command_buffer.h"
+#include "gpu/command_buffer/common/sync_token.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+using gpu::gles2::QueryTracker;
+using testing::_;
+using testing::AtLeast;
+using testing::AnyNumber;
+using testing::DoAll;
+using testing::InSequence;
+using testing::Invoke;
+using testing::Mock;
+using testing::Sequence;
+using testing::StrictMock;
+using testing::Return;
+using testing::ReturnRef;
+
+namespace gpu {
+namespace raster {
+
+ACTION_P2(SetMemory, dst, obj) {
+  memcpy(dst, &obj, sizeof(obj));
+}
+
+ACTION_P3(SetMemoryFromArray, dst, array, size) {
+  memcpy(dst, array, size);
+}
+
+// Used to help set the transfer buffer result to SizedResult of a single value.
+template <typename T>
+class SizedResultHelper {
+ public:
+  explicit SizedResultHelper(T result) : size_(sizeof(result)) {
+    memcpy(result_, &result, sizeof(T));
+  }
+
+ private:
+  uint32_t size_;
+  char result_[sizeof(T)];
+};
+
+class RasterImplementationTest : public testing::Test {
+ protected:
+  static const uint8_t kInitialValue = 0xBD;
+  static const int32_t kNumCommandEntries = 500;
+  static const int32_t kCommandBufferSizeBytes =
+      kNumCommandEntries * sizeof(CommandBufferEntry);
+  static const size_t kTransferBufferSize = 512;
+
+  static const GLint kMaxCombinedTextureImageUnits = 8;
+  static const GLint kMaxTextureImageUnits = 8;
+  static const GLint kMaxTextureSize = 128;
+  static const GLint kNumCompressedTextureFormats = 0;
+  static const GLuint kStartId = 1024;
+  static const GLuint kBuffersStartId = 1;
+  static const GLuint kTexturesStartId = 1;
+  static const GLuint kQueriesStartId = 1;
+
+  typedef MockTransferBuffer::ExpectedMemoryInfo ExpectedMemoryInfo;
+
+  class TestContext {
+   public:
+    TestContext() : commands_(nullptr), token_(0) {}
+
+    bool Initialize(bool bind_generates_resource_client,
+                    bool bind_generates_resource_service,
+                    bool lose_context_when_out_of_memory,
+                    bool transfer_buffer_initialize_fail,
+                    bool sync_query) {
+      SharedMemoryLimits limits = SharedMemoryLimitsForTesting();
+      command_buffer_.reset(new StrictMock<MockClientCommandBuffer>());
+
+      transfer_buffer_.reset(new MockTransferBuffer(
+          command_buffer_.get(), kTransferBufferSize,
+          RasterImplementation::kStartingOffset,
+          RasterImplementation::kAlignment, transfer_buffer_initialize_fail));
+
+      helper_.reset(new RasterCmdHelper(command_buffer()));
+      helper_->Initialize(limits.command_buffer_size);
+
+      gpu_control_.reset(new StrictMock<MockClientGpuControl>());
+      capabilities_.max_combined_texture_image_units =
+          kMaxCombinedTextureImageUnits;
+      capabilities_.max_texture_image_units = kMaxTextureImageUnits;
+      capabilities_.max_texture_size = kMaxTextureSize;
+      capabilities_.num_compressed_texture_formats =
+          kNumCompressedTextureFormats;
+      capabilities_.bind_generates_resource_chromium =
+          bind_generates_resource_service ? 1 : 0;
+      capabilities_.sync_query = sync_query;
+      EXPECT_CALL(*gpu_control_, GetCapabilities())
+          .WillOnce(ReturnRef(capabilities_));
+
+      {
+        InSequence sequence;
+
+        gl_.reset(new RasterImplementation(
+            helper_.get(), transfer_buffer_.get(),
+            bind_generates_resource_client, lose_context_when_out_of_memory,
+            gpu_control_.get()));
+      }
+
+      // The client should be set to something non-null.
+      EXPECT_CALL(*gpu_control_, SetGpuControlClient(gl_.get())).Times(1);
+
+      if (gl_->Initialize(limits) != gpu::ContextResult::kSuccess)
+        return false;
+
+      helper_->CommandBufferHelper::Finish();
+      Mock::VerifyAndClearExpectations(gl_.get());
+
+      scoped_refptr<Buffer> ring_buffer = helper_->get_ring_buffer();
+      commands_ = static_cast<CommandBufferEntry*>(ring_buffer->memory()) +
+                  command_buffer()->GetServicePutOffset();
+      ClearCommands();
+      EXPECT_TRUE(transfer_buffer_->InSync());
+
+      Mock::VerifyAndClearExpectations(command_buffer());
+      return true;
+    }
+
+    void TearDown() {
+      Mock::VerifyAndClear(gl_.get());
+      EXPECT_CALL(*command_buffer(), OnFlush()).Times(AnyNumber());
+      // For command buffer.
+      EXPECT_CALL(*command_buffer(), DestroyTransferBuffer(_))
+          .Times(AtLeast(1));
+      // The client should be unset.
+      EXPECT_CALL(*gpu_control_, SetGpuControlClient(nullptr)).Times(1);
+      gl_.reset();
+    }
+
+    MockClientCommandBuffer* command_buffer() const {
+      return command_buffer_.get();
+    }
+
+    int GetNextToken() { return ++token_; }
+
+    void ClearCommands() {
+      scoped_refptr<Buffer> ring_buffer = helper_->get_ring_buffer();
+      memset(ring_buffer->memory(), kInitialValue, ring_buffer->size());
+    }
+
+    std::unique_ptr<MockClientCommandBuffer> command_buffer_;
+    std::unique_ptr<MockClientGpuControl> gpu_control_;
+    std::unique_ptr<RasterCmdHelper> helper_;
+    std::unique_ptr<MockTransferBuffer> transfer_buffer_;
+    std::unique_ptr<RasterImplementation> gl_;
+    CommandBufferEntry* commands_;
+    int token_;
+    Capabilities capabilities_;
+  };
+
+  RasterImplementationTest() : commands_(nullptr) {}
+
+  void SetUp() override;
+  void TearDown() override;
+
+  bool NoCommandsWritten() {
+    scoped_refptr<Buffer> ring_buffer = helper_->get_ring_buffer();
+    const uint8_t* cmds =
+        reinterpret_cast<const uint8_t*>(ring_buffer->memory());
+    const uint8_t* end = cmds + ring_buffer->size();
+    for (; cmds < end; ++cmds) {
+      if (*cmds != kInitialValue) {
+        return false;
+      }
+    }
+    return true;
+  }
+
+  QueryTracker::Query* GetQuery(GLuint id) {
+    return gl_->query_tracker_->GetQuery(id);
+  }
+
+  QueryTracker* GetQueryTracker() { return gl_->query_tracker_.get(); }
+
+  ClientDiscardableTextureManager* discardable_texture_manager() {
+    return &gl_->discardable_texture_manager_;
+  }
+
+  void* MapRasterCHROMIUM(GLsizeiptr size) {
+    return gl_->MapRasterCHROMIUM(size);
+  }
+  void UnmapRasterCHROMIUM(GLsizeiptr written_size) {
+    gl_->UnmapRasterCHROMIUM(written_size);
+  }
+
+  struct ContextInitOptions {
+    ContextInitOptions()
+        : bind_generates_resource_client(true),
+          bind_generates_resource_service(true),
+          lose_context_when_out_of_memory(false),
+          transfer_buffer_initialize_fail(false),
+          sync_query(true) {}
+    bool bind_generates_resource_client;
+    bool bind_generates_resource_service;
+    bool lose_context_when_out_of_memory;
+    bool transfer_buffer_initialize_fail;
+    bool sync_query;
+  };
+
+  bool Initialize(const ContextInitOptions& init_options) {
+    bool success = true;
+    if (!test_context_.Initialize(init_options.bind_generates_resource_client,
+                                  init_options.bind_generates_resource_service,
+                                  init_options.lose_context_when_out_of_memory,
+                                  init_options.transfer_buffer_initialize_fail,
+                                  init_options.sync_query)) {
+      success = false;
+    }
+
+    // Default to test context 0.
+    gpu_control_ = test_context_.gpu_control_.get();
+    helper_ = test_context_.helper_.get();
+    transfer_buffer_ = test_context_.transfer_buffer_.get();
+    gl_ = test_context_.gl_.get();
+    commands_ = test_context_.commands_;
+    return success;
+  }
+
+  MockClientCommandBuffer* command_buffer() const {
+    return test_context_.command_buffer_.get();
+  }
+
+  int GetNextToken() { return test_context_.GetNextToken(); }
+
+  const void* GetPut() { return helper_->GetSpace(0); }
+
+  void ClearCommands() {
+    scoped_refptr<Buffer> ring_buffer = helper_->get_ring_buffer();
+    memset(ring_buffer->memory(), kInitialValue, ring_buffer->size());
+  }
+
+  size_t MaxTransferBufferSize() {
+    return transfer_buffer_->MaxTransferBufferSize();
+  }
+
+  void SetMappedMemoryLimit(size_t limit) {
+    gl_->mapped_memory_->set_max_allocated_bytes(limit);
+  }
+
+  ExpectedMemoryInfo GetExpectedMemory(size_t size) {
+    return transfer_buffer_->GetExpectedMemory(size);
+  }
+
+  ExpectedMemoryInfo GetExpectedResultMemory(size_t size) {
+    return transfer_buffer_->GetExpectedResultMemory(size);
+  }
+
+  ExpectedMemoryInfo GetExpectedMappedMemory(size_t size) {
+    ExpectedMemoryInfo mem;
+
+    // Temporarily allocate memory and expect that memory block to be reused.
+    mem.ptr = static_cast<uint8_t*>(
+        gl_->mapped_memory_->Alloc(size, &mem.id, &mem.offset));
+    gl_->mapped_memory_->Free(mem.ptr);
+
+    return mem;
+  }
+
+  int CheckError() {
+    ExpectedMemoryInfo result =
+        GetExpectedResultMemory(sizeof(cmds::GetError::Result));
+    EXPECT_CALL(*command_buffer(), OnFlush())
+        .WillOnce(SetMemory(result.ptr, GLuint(GL_NO_ERROR)))
+        .RetiresOnSaturation();
+    return gl_->GetError();
+  }
+
+  const std::string& GetLastError() { return gl_->GetLastError(); }
+
+  bool GetBucketContents(uint32_t bucket_id, std::vector<int8_t>* data) {
+    return gl_->GetBucketContents(bucket_id, data);
+  }
+
+  static SharedMemoryLimits SharedMemoryLimitsForTesting() {
+    SharedMemoryLimits limits;
+    limits.command_buffer_size = kCommandBufferSizeBytes;
+    limits.start_transfer_buffer_size = kTransferBufferSize;
+    limits.min_transfer_buffer_size = kTransferBufferSize;
+    limits.max_transfer_buffer_size = kTransferBufferSize;
+    limits.mapped_memory_reclaim_limit = SharedMemoryLimits::kNoLimit;
+    return limits;
+  }
+
+  TestContext test_context_;
+
+  MockClientGpuControl* gpu_control_;
+  RasterCmdHelper* helper_;
+  MockTransferBuffer* transfer_buffer_;
+  RasterImplementation* gl_;
+  CommandBufferEntry* commands_;
+};
+
+void RasterImplementationTest::SetUp() {
+  ContextInitOptions init_options;
+  ASSERT_TRUE(Initialize(init_options));
+}
+
+void RasterImplementationTest::TearDown() {
+  test_context_.TearDown();
+}
+
+class RasterImplementationManualInitTest : public RasterImplementationTest {
+ protected:
+  void SetUp() override {}
+};
+
+// GCC requires these declarations, but MSVC requires they not be present
+#ifndef _MSC_VER
+const uint8_t RasterImplementationTest::kInitialValue;
+const int32_t RasterImplementationTest::kNumCommandEntries;
+const int32_t RasterImplementationTest::kCommandBufferSizeBytes;
+const size_t RasterImplementationTest::kTransferBufferSize;
+const GLint RasterImplementationTest::kMaxCombinedTextureImageUnits;
+const GLint RasterImplementationTest::kMaxTextureImageUnits;
+const GLint RasterImplementationTest::kMaxTextureSize;
+const GLint RasterImplementationTest::kNumCompressedTextureFormats;
+const GLuint RasterImplementationTest::kStartId;
+const GLuint RasterImplementationTest::kBuffersStartId;
+const GLuint RasterImplementationTest::kTexturesStartId;
+const GLuint RasterImplementationTest::kQueriesStartId;
+#endif
+
+TEST_F(RasterImplementationTest, GetBucketContents) {
+  const uint32_t kBucketId = RasterImplementation::kResultBucketId;
+  const uint32_t kTestSize = MaxTransferBufferSize() + 32;
+
+  std::unique_ptr<uint8_t[]> buf(new uint8_t[kTestSize]);
+  uint8_t* expected_data = buf.get();
+  for (uint32_t ii = 0; ii < kTestSize; ++ii) {
+    expected_data[ii] = ii * 3;
+  }
+
+  struct Cmds {
+    cmd::GetBucketStart get_bucket_start;
+    cmd::SetToken set_token1;
+    cmd::GetBucketData get_bucket_data;
+    cmd::SetToken set_token2;
+    cmd::SetBucketSize set_bucket_size2;
+  };
+
+  ExpectedMemoryInfo mem1 = GetExpectedMemory(MaxTransferBufferSize());
+  ExpectedMemoryInfo result1 = GetExpectedResultMemory(sizeof(uint32_t));
+  ExpectedMemoryInfo mem2 =
+      GetExpectedMemory(kTestSize - MaxTransferBufferSize());
+
+  Cmds expected;
+  expected.get_bucket_start.Init(kBucketId, result1.id, result1.offset,
+                                 MaxTransferBufferSize(), mem1.id, mem1.offset);
+  expected.set_token1.Init(GetNextToken());
+  expected.get_bucket_data.Init(kBucketId, MaxTransferBufferSize(),
+                                kTestSize - MaxTransferBufferSize(), mem2.id,
+                                mem2.offset);
+  expected.set_bucket_size2.Init(kBucketId, 0);
+  expected.set_token2.Init(GetNextToken());
+
+  EXPECT_CALL(*command_buffer(), OnFlush())
+      .WillOnce(DoAll(
+          SetMemory(result1.ptr, kTestSize),
+          SetMemoryFromArray(mem1.ptr, expected_data, MaxTransferBufferSize())))
+      .WillOnce(SetMemoryFromArray(mem2.ptr,
+                                   expected_data + MaxTransferBufferSize(),
+                                   kTestSize - MaxTransferBufferSize()))
+      .RetiresOnSaturation();
+
+  std::vector<int8_t> data;
+  GetBucketContents(kBucketId, &data);
+  EXPECT_EQ(0, memcmp(&expected, commands_, sizeof(expected)));
+  ASSERT_EQ(kTestSize, data.size());
+  EXPECT_EQ(0, memcmp(expected_data, &data[0], data.size()));
+}
+
+// Test that things are cached
+TEST_F(RasterImplementationTest, GetIntegerCacheRead) {
+  struct PNameValue {
+    GLenum pname;
+    GLint expected;
+  };
+  const PNameValue pairs[] = {{
+                                  GL_ACTIVE_TEXTURE, GL_TEXTURE0,
+                              },
+                              {
+                                  GL_TEXTURE_BINDING_2D, 0,
+                              }};
+  size_t num_pairs = sizeof(pairs) / sizeof(pairs[0]);
+  for (size_t ii = 0; ii < num_pairs; ++ii) {
+    const PNameValue& pv = pairs[ii];
+    GLint v = -1;
+    gl_->GetIntegerv(pv.pname, &v);
+    EXPECT_TRUE(NoCommandsWritten());
+    EXPECT_EQ(pv.expected, v);
+  }
+
+  ExpectedMemoryInfo result1 =
+      GetExpectedResultMemory(sizeof(cmds::GetError::Result));
+
+  EXPECT_CALL(*command_buffer(), OnFlush())
+      .WillOnce(SetMemory(result1.ptr, GLuint(GL_NO_ERROR)))
+      .RetiresOnSaturation();
+  EXPECT_EQ(static_cast<GLenum>(GL_NO_ERROR), gl_->GetError());
+}
+
+TEST_F(RasterImplementationTest, GetIntegerCacheWrite) {
+  struct PNameValue {
+    GLenum pname;
+    GLint expected;
+  };
+  gl_->ActiveTexture(GL_TEXTURE4);
+  gl_->BindTexture(GL_TEXTURE_2D, 6);
+
+  const PNameValue pairs[] = {{
+                                  GL_ACTIVE_TEXTURE, GL_TEXTURE4,
+                              },
+                              {
+                                  GL_TEXTURE_BINDING_2D, 6,
+                              }};
+  size_t num_pairs = sizeof(pairs) / sizeof(pairs[0]);
+  for (size_t ii = 0; ii < num_pairs; ++ii) {
+    const PNameValue& pv = pairs[ii];
+    GLint v = -1;
+    gl_->GetIntegerv(pv.pname, &v);
+    EXPECT_EQ(pv.expected, v);
+  }
+
+  ExpectedMemoryInfo result1 =
+      GetExpectedResultMemory(sizeof(cmds::GetError::Result));
+
+  EXPECT_CALL(*command_buffer(), OnFlush())
+      .WillOnce(SetMemory(result1.ptr, GLuint(GL_NO_ERROR)))
+      .RetiresOnSaturation();
+  EXPECT_EQ(static_cast<GLenum>(GL_NO_ERROR), gl_->GetError());
+}
+
+TEST_F(RasterImplementationTest, BeginEndQueryEXT) {
+  //  GL_COMMANDS_COMPLETED_CHROMIUM,
+  //  GL_CURRENT_QUERY_EXT
+
+  GLuint expected_ids[2] = {1, 2};  // These must match what's actually genned.
+  struct GenCmds {
+    cmds::GenQueriesEXTImmediate gen;
+    GLuint data[2];
+  };
+  GenCmds expected_gen_cmds;
+  expected_gen_cmds.gen.Init(arraysize(expected_ids), &expected_ids[0]);
+  GLuint ids[arraysize(expected_ids)] = {
+      0,
+  };
+  gl_->GenQueriesEXT(arraysize(expected_ids), &ids[0]);
+  EXPECT_EQ(0,
+            memcmp(&expected_gen_cmds, commands_, sizeof(expected_gen_cmds)));
+  GLuint id1 = ids[0];
+  GLuint id2 = ids[1];
+  ClearCommands();
+
+  // Test BeginQueryEXT fails if id = 0.
+  gl_->BeginQueryEXT(GL_COMMANDS_COMPLETED_CHROMIUM, 0);
+  EXPECT_TRUE(NoCommandsWritten());
+  EXPECT_EQ(GL_INVALID_OPERATION, CheckError());
+
+  // Test BeginQueryEXT inserts command.
+  struct BeginCmds {
+    cmds::BeginQueryEXT begin_query;
+  };
+  BeginCmds expected_begin_cmds;
+  const void* commands = GetPut();
+  gl_->BeginQueryEXT(GL_COMMANDS_COMPLETED_CHROMIUM, id1);
+  QueryTracker::Query* query = GetQuery(id1);
+  ASSERT_TRUE(query != nullptr);
+  expected_begin_cmds.begin_query.Init(GL_COMMANDS_COMPLETED_CHROMIUM, id1,
+                                       query->shm_id(), query->shm_offset());
+  EXPECT_EQ(
+      0, memcmp(&expected_begin_cmds, commands, sizeof(expected_begin_cmds)));
+  ClearCommands();
+
+  // Test BeginQueryEXT fails if between Begin/End.
+  gl_->BeginQueryEXT(GL_COMMANDS_COMPLETED_CHROMIUM, id2);
+  EXPECT_TRUE(NoCommandsWritten());
+  EXPECT_EQ(GL_INVALID_OPERATION, CheckError());
+
+  // Test EndQueryEXT sends command
+  struct EndCmds {
+    cmds::EndQueryEXT end_query;
+  };
+  commands = GetPut();
+  gl_->EndQueryEXT(GL_COMMANDS_COMPLETED_CHROMIUM);
+  EndCmds expected_end_cmds;
+  expected_end_cmds.end_query.Init(GL_COMMANDS_COMPLETED_CHROMIUM,
+                                   query->submit_count());
+  EXPECT_EQ(0, memcmp(&expected_end_cmds, commands, sizeof(expected_end_cmds)));
+
+  // Test EndQueryEXT fails if no current query.
+  ClearCommands();
+  gl_->EndQueryEXT(GL_COMMANDS_COMPLETED_CHROMIUM);
+  EXPECT_TRUE(NoCommandsWritten());
+  EXPECT_EQ(GL_INVALID_OPERATION, CheckError());
+
+  // Test 2nd Begin/End increments count.
+  base::subtle::Atomic32 old_submit_count = query->submit_count();
+  gl_->BeginQueryEXT(GL_COMMANDS_COMPLETED_CHROMIUM, id1);
+  EXPECT_EQ(old_submit_count, query->submit_count());
+  commands = GetPut();
+  gl_->EndQueryEXT(GL_COMMANDS_COMPLETED_CHROMIUM);
+  EXPECT_NE(old_submit_count, query->submit_count());
+  expected_end_cmds.end_query.Init(GL_COMMANDS_COMPLETED_CHROMIUM,
+                                   query->submit_count());
+  EXPECT_EQ(0, memcmp(&expected_end_cmds, commands, sizeof(expected_end_cmds)));
+
+  // Test GetQueryObjectuivEXT fails if unused id
+  GLuint available = 0xBDu;
+  ClearCommands();
+  gl_->GetQueryObjectuivEXT(id2, GL_QUERY_RESULT_AVAILABLE_EXT, &available);
+  EXPECT_TRUE(NoCommandsWritten());
+  EXPECT_EQ(0xBDu, available);
+  EXPECT_EQ(GL_INVALID_OPERATION, CheckError());
+
+  // Test GetQueryObjectuivEXT fails if bad id
+  ClearCommands();
+  gl_->GetQueryObjectuivEXT(4567, GL_QUERY_RESULT_AVAILABLE_EXT, &available);
+  EXPECT_TRUE(NoCommandsWritten());
+  EXPECT_EQ(0xBDu, available);
+  EXPECT_EQ(GL_INVALID_OPERATION, CheckError());
+
+  // Test GetQueryObjectuivEXT CheckResultsAvailable
+  ClearCommands();
+  gl_->GetQueryObjectuivEXT(id1, GL_QUERY_RESULT_AVAILABLE_EXT, &available);
+  EXPECT_EQ(0u, available);
+}
+
+TEST_F(RasterImplementationManualInitTest, BadQueryTargets) {
+  ContextInitOptions init_options;
+  init_options.sync_query = false;
+  ASSERT_TRUE(Initialize(init_options));
+
+  GLuint id = 0;
+  gl_->GenQueriesEXT(1, &id);
+  ClearCommands();
+
+  gl_->BeginQueryEXT(GL_COMMANDS_COMPLETED_CHROMIUM, id);
+  EXPECT_EQ(GL_INVALID_OPERATION, CheckError());
+  EXPECT_EQ(nullptr, GetQuery(id));
+
+  gl_->BeginQueryEXT(0x123, id);
+  EXPECT_EQ(GL_INVALID_ENUM, CheckError());
+  EXPECT_EQ(nullptr, GetQuery(id));
+}
+
+TEST_F(RasterImplementationTest, GenSyncTokenCHROMIUM) {
+  const CommandBufferNamespace kNamespaceId = CommandBufferNamespace::GPU_IO;
+  const CommandBufferId kCommandBufferId =
+      CommandBufferId::FromUnsafeValue(234u);
+  const GLuint64 kFenceSync = 123u;
+  SyncToken sync_token;
+
+  EXPECT_CALL(*gpu_control_, GetNamespaceID())
+      .WillRepeatedly(Return(kNamespaceId));
+  EXPECT_CALL(*gpu_control_, GetCommandBufferID())
+      .WillRepeatedly(Return(kCommandBufferId));
+
+  gl_->GenSyncTokenCHROMIUM(nullptr);
+  EXPECT_TRUE(NoCommandsWritten());
+  EXPECT_EQ(GL_INVALID_VALUE, CheckError());
+
+  const void* commands = GetPut();
+  cmds::InsertFenceSyncCHROMIUM insert_fence_sync;
+  insert_fence_sync.Init(kFenceSync);
+
+  EXPECT_CALL(*gpu_control_, GenerateFenceSyncRelease())
+      .WillOnce(Return(kFenceSync));
+  EXPECT_CALL(*gpu_control_, EnsureWorkVisible());
+  gl_->GenSyncTokenCHROMIUM(sync_token.GetData());
+  EXPECT_EQ(0, memcmp(&insert_fence_sync, commands, sizeof(insert_fence_sync)));
+  EXPECT_EQ(GL_NO_ERROR, CheckError());
+
+  EXPECT_TRUE(sync_token.verified_flush());
+  EXPECT_EQ(kNamespaceId, sync_token.namespace_id());
+  EXPECT_EQ(kCommandBufferId, sync_token.command_buffer_id());
+  EXPECT_EQ(kFenceSync, sync_token.release_count());
+}
+
+TEST_F(RasterImplementationTest, GenUnverifiedSyncTokenCHROMIUM) {
+  const CommandBufferNamespace kNamespaceId = CommandBufferNamespace::GPU_IO;
+  const CommandBufferId kCommandBufferId =
+      CommandBufferId::FromUnsafeValue(234u);
+  const GLuint64 kFenceSync = 123u;
+  SyncToken sync_token;
+
+  EXPECT_CALL(*gpu_control_, GetNamespaceID())
+      .WillRepeatedly(Return(kNamespaceId));
+  EXPECT_CALL(*gpu_control_, GetCommandBufferID())
+      .WillRepeatedly(Return(kCommandBufferId));
+
+  gl_->GenUnverifiedSyncTokenCHROMIUM(nullptr);
+  EXPECT_TRUE(NoCommandsWritten());
+  EXPECT_EQ(GL_INVALID_VALUE, CheckError());
+
+  const void* commands = GetPut();
+  cmds::InsertFenceSyncCHROMIUM insert_fence_sync;
+  insert_fence_sync.Init(kFenceSync);
+
+  EXPECT_CALL(*gpu_control_, GenerateFenceSyncRelease())
+      .WillOnce(Return(kFenceSync));
+  gl_->GenUnverifiedSyncTokenCHROMIUM(sync_token.GetData());
+  EXPECT_EQ(0, memcmp(&insert_fence_sync, commands, sizeof(insert_fence_sync)));
+  EXPECT_EQ(GL_NO_ERROR, CheckError());
+
+  EXPECT_FALSE(sync_token.verified_flush());
+  EXPECT_EQ(kNamespaceId, sync_token.namespace_id());
+  EXPECT_EQ(kCommandBufferId, sync_token.command_buffer_id());
+  EXPECT_EQ(kFenceSync, sync_token.release_count());
+}
+
+TEST_F(RasterImplementationTest, VerifySyncTokensCHROMIUM) {
+  ExpectedMemoryInfo result =
+      GetExpectedResultMemory(sizeof(cmds::GetError::Result));
+  EXPECT_CALL(*command_buffer(), OnFlush())
+      .WillRepeatedly(SetMemory(result.ptr, GLuint(GL_NO_ERROR)))
+      .RetiresOnSaturation();
+
+  const CommandBufferNamespace kNamespaceId = CommandBufferNamespace::GPU_IO;
+  const CommandBufferId kCommandBufferId =
+      CommandBufferId::FromUnsafeValue(234u);
+  const GLuint64 kFenceSync = 123u;
+  gpu::SyncToken sync_token;
+  GLbyte* sync_token_datas[] = {sync_token.GetData()};
+
+  EXPECT_CALL(*gpu_control_, GetNamespaceID())
+      .WillRepeatedly(Return(kNamespaceId));
+  EXPECT_CALL(*gpu_control_, GetCommandBufferID())
+      .WillRepeatedly(Return(kCommandBufferId));
+
+  EXPECT_CALL(*gpu_control_, GenerateFenceSyncRelease())
+      .WillOnce(Return(kFenceSync));
+  gl_->GenUnverifiedSyncTokenCHROMIUM(sync_token.GetData());
+  EXPECT_TRUE(sync_token.HasData());
+  EXPECT_FALSE(sync_token.verified_flush());
+
+  ClearCommands();
+  EXPECT_CALL(*gpu_control_, CanWaitUnverifiedSyncToken(sync_token))
+      .WillOnce(Return(false));
+  gl_->VerifySyncTokensCHROMIUM(sync_token_datas, 1);
+  EXPECT_TRUE(NoCommandsWritten());
+  EXPECT_EQ(static_cast<GLenum>(GL_INVALID_VALUE), gl_->GetError());
+  EXPECT_FALSE(sync_token.verified_flush());
+
+  ClearCommands();
+  EXPECT_CALL(*gpu_control_, CanWaitUnverifiedSyncToken(sync_token))
+      .WillOnce(Return(true));
+  EXPECT_CALL(*gpu_control_, EnsureWorkVisible());
+  gl_->VerifySyncTokensCHROMIUM(sync_token_datas, arraysize(sync_token_datas));
+  EXPECT_TRUE(NoCommandsWritten());
+  EXPECT_EQ(GL_NO_ERROR, CheckError());
+
+  EXPECT_EQ(kNamespaceId, sync_token.namespace_id());
+  EXPECT_EQ(kCommandBufferId, sync_token.command_buffer_id());
+  EXPECT_EQ(kFenceSync, sync_token.release_count());
+  EXPECT_TRUE(sync_token.verified_flush());
+}
+
+TEST_F(RasterImplementationTest, VerifySyncTokensCHROMIUM_Sequence) {
+  // To verify sync tokens, the sync tokens must all be verified after
+  // CanWaitUnverifiedSyncTokens() are called. This test ensures the right
+  // sequence.
+  ExpectedMemoryInfo result =
+      GetExpectedResultMemory(sizeof(cmds::GetError::Result));
+  EXPECT_CALL(*command_buffer(), OnFlush())
+      .WillRepeatedly(SetMemory(result.ptr, GLuint(GL_NO_ERROR)))
+      .RetiresOnSaturation();
+
+  const CommandBufferNamespace kNamespaceId = CommandBufferNamespace::GPU_IO;
+  const CommandBufferId kCommandBufferId =
+      CommandBufferId::FromUnsafeValue(234u);
+  const GLuint64 kFenceSync1 = 123u;
+  const GLuint64 kFenceSync2 = 234u;
+  gpu::SyncToken sync_token1;
+  gpu::SyncToken sync_token2;
+  GLbyte* sync_token_datas[] = {sync_token1.GetData(), sync_token2.GetData()};
+
+  EXPECT_CALL(*gpu_control_, GetNamespaceID())
+      .WillRepeatedly(Return(kNamespaceId));
+  EXPECT_CALL(*gpu_control_, GetCommandBufferID())
+      .WillRepeatedly(Return(kCommandBufferId));
+
+  // Generate sync token 1.
+  EXPECT_CALL(*gpu_control_, GenerateFenceSyncRelease())
+      .WillOnce(Return(kFenceSync1));
+  gl_->GenUnverifiedSyncTokenCHROMIUM(sync_token1.GetData());
+  EXPECT_TRUE(sync_token1.HasData());
+  EXPECT_FALSE(sync_token1.verified_flush());
+
+  // Generate sync token 2.
+  EXPECT_CALL(*gpu_control_, GenerateFenceSyncRelease())
+      .WillOnce(Return(kFenceSync2));
+  gl_->GenUnverifiedSyncTokenCHROMIUM(sync_token2.GetData());
+  EXPECT_TRUE(sync_token2.HasData());
+  EXPECT_FALSE(sync_token2.verified_flush());
+
+  // Ensure proper sequence of checking and validating.
+  Sequence sequence;
+  EXPECT_CALL(*gpu_control_, CanWaitUnverifiedSyncToken(sync_token1))
+      .InSequence(sequence)
+      .WillOnce(Return(true));
+  EXPECT_CALL(*gpu_control_, CanWaitUnverifiedSyncToken(sync_token2))
+      .InSequence(sequence)
+      .WillOnce(Return(true));
+  EXPECT_CALL(*gpu_control_, EnsureWorkVisible()).InSequence(sequence);
+  gl_->VerifySyncTokensCHROMIUM(sync_token_datas, arraysize(sync_token_datas));
+  EXPECT_EQ(GL_NO_ERROR, CheckError());
+
+  EXPECT_TRUE(sync_token1.verified_flush());
+  EXPECT_TRUE(sync_token2.verified_flush());
+}
+
+TEST_F(RasterImplementationTest, VerifySyncTokensCHROMIUM_EmptySyncToken) {
+  // To verify sync tokens, the sync tokens must all be verified after
+  // CanWaitUnverifiedSyncTokens() are called. This test ensures the right
+  // sequence.
+  ExpectedMemoryInfo result =
+      GetExpectedResultMemory(sizeof(cmds::GetError::Result));
+  EXPECT_CALL(*command_buffer(), OnFlush())
+      .WillRepeatedly(SetMemory(result.ptr, GLuint(GL_NO_ERROR)))
+      .RetiresOnSaturation();
+
+  gpu::SyncToken sync_token1, sync_token2;
+  GLbyte* sync_token_datas[] = {sync_token1.GetData(), sync_token2.GetData()};
+
+  // Ensure proper sequence of checking and validating.
+  EXPECT_CALL(*gpu_control_, CanWaitUnverifiedSyncToken(_)).Times(0);
+  EXPECT_CALL(*gpu_control_, EnsureWorkVisible()).Times(0);
+  gl_->VerifySyncTokensCHROMIUM(sync_token_datas, arraysize(sync_token_datas));
+  EXPECT_TRUE(NoCommandsWritten());
+  EXPECT_EQ(GL_NO_ERROR, CheckError());
+
+  EXPECT_TRUE(sync_token1.verified_flush());
+  EXPECT_TRUE(sync_token2.verified_flush());
+}
+
+TEST_F(RasterImplementationTest, WaitSyncTokenCHROMIUM) {
+  const CommandBufferNamespace kNamespaceId = CommandBufferNamespace::GPU_IO;
+  const CommandBufferId kCommandBufferId =
+      CommandBufferId::FromUnsafeValue(234u);
+  const GLuint64 kFenceSync = 456u;
+
+  gpu::SyncToken sync_token;
+  GLbyte* sync_token_data = sync_token.GetData();
+
+  struct Cmds {
+    cmds::InsertFenceSyncCHROMIUM insert_fence_sync;
+    cmds::WaitSyncTokenCHROMIUM wait_sync_token;
+  };
+  Cmds expected;
+  expected.insert_fence_sync.Init(kFenceSync);
+  expected.wait_sync_token.Init(kNamespaceId, kCommandBufferId.GetUnsafeValue(),
+                                kFenceSync);
+
+  EXPECT_CALL(*gpu_control_, GetNamespaceID()).WillOnce(Return(kNamespaceId));
+  EXPECT_CALL(*gpu_control_, GetCommandBufferID())
+      .WillOnce(Return(kCommandBufferId));
+  EXPECT_CALL(*gpu_control_, GenerateFenceSyncRelease())
+      .WillOnce(Return(kFenceSync));
+  EXPECT_CALL(*gpu_control_, EnsureWorkVisible());
+  gl_->GenSyncTokenCHROMIUM(sync_token_data);
+
+  EXPECT_CALL(*gpu_control_, WaitSyncTokenHint(sync_token));
+  gl_->WaitSyncTokenCHROMIUM(sync_token_data);
+  EXPECT_EQ(0, memcmp(&expected, commands_, sizeof(expected)));
+}
+
+TEST_F(RasterImplementationTest, WaitSyncTokenCHROMIUMErrors) {
+  ExpectedMemoryInfo result =
+      GetExpectedResultMemory(sizeof(cmds::GetError::Result));
+  EXPECT_CALL(*command_buffer(), OnFlush())
+      .WillRepeatedly(SetMemory(result.ptr, GLuint(GL_NO_ERROR)))
+      .RetiresOnSaturation();
+
+  // Empty sync tokens should be produce no error and be a nop.
+  ClearCommands();
+  gl_->WaitSyncTokenCHROMIUM(nullptr);
+  EXPECT_TRUE(NoCommandsWritten());
+  EXPECT_EQ(static_cast<GLenum>(GL_NO_ERROR), gl_->GetError());
+
+  // Invalid sync tokens should produce no error and be a nop.
+  ClearCommands();
+  gpu::SyncToken invalid_sync_token;
+  gl_->WaitSyncTokenCHROMIUM(invalid_sync_token.GetConstData());
+  EXPECT_TRUE(NoCommandsWritten());
+  EXPECT_EQ(static_cast<GLenum>(GL_NO_ERROR), gl_->GetError());
+
+  // Unverified sync token should produce INVALID_OPERATION.
+  ClearCommands();
+  gpu::SyncToken unverified_sync_token(CommandBufferNamespace::GPU_IO,
+                                       gpu::CommandBufferId(), 0);
+  EXPECT_CALL(*gpu_control_, CanWaitUnverifiedSyncToken(unverified_sync_token))
+      .WillOnce(Return(false));
+  gl_->WaitSyncTokenCHROMIUM(unverified_sync_token.GetConstData());
+  EXPECT_TRUE(NoCommandsWritten());
+  EXPECT_EQ(static_cast<GLenum>(GL_INVALID_VALUE), gl_->GetError());
+}
+
+static void CountCallback(int* count) {
+  (*count)++;
+}
+
+TEST_F(RasterImplementationTest, SignalSyncToken) {
+  const CommandBufferNamespace kNamespaceId = CommandBufferNamespace::GPU_IO;
+  const CommandBufferId kCommandBufferId = CommandBufferId::FromUnsafeValue(1);
+  const uint64_t kFenceSync = 123u;
+
+  EXPECT_CALL(*gpu_control_, GetNamespaceID())
+      .WillRepeatedly(Return(kNamespaceId));
+  EXPECT_CALL(*gpu_control_, GetCommandBufferID())
+      .WillRepeatedly(Return(kCommandBufferId));
+
+  EXPECT_CALL(*gpu_control_, GenerateFenceSyncRelease())
+      .WillOnce(Return(kFenceSync));
+  EXPECT_CALL(*gpu_control_, EnsureWorkVisible());
+  gpu::SyncToken sync_token;
+  gl_->GenSyncTokenCHROMIUM(sync_token.GetData());
+
+  int signaled_count = 0;
+
+  // Request a signal sync token, which gives a callback to the GpuControl to
+  // run when the sync token is reached.
+  base::OnceClosure signal_closure;
+  EXPECT_CALL(*gpu_control_, DoSignalSyncToken(_, _))
+      .WillOnce(Invoke([&signal_closure](const SyncToken& sync_token,
+                                         base::OnceClosure* callback) {
+        signal_closure = std::move(*callback);
+      }));
+  gl_->SignalSyncToken(sync_token,
+                       base::BindOnce(&CountCallback, &signaled_count));
+  EXPECT_EQ(0, signaled_count);
+
+  // When GpuControl runs the callback, the original callback we gave to
+  // RasterImplementation is run.
+  std::move(signal_closure).Run();
+  EXPECT_EQ(1, signaled_count);
+}
+
+TEST_F(RasterImplementationTest, SignalSyncTokenAfterContextLoss) {
+  const CommandBufferNamespace kNamespaceId = CommandBufferNamespace::GPU_IO;
+  const CommandBufferId kCommandBufferId = CommandBufferId::FromUnsafeValue(1);
+  const uint64_t kFenceSync = 123u;
+
+  EXPECT_CALL(*gpu_control_, GetNamespaceID()).WillOnce(Return(kNamespaceId));
+  EXPECT_CALL(*gpu_control_, GetCommandBufferID())
+      .WillOnce(Return(kCommandBufferId));
+  EXPECT_CALL(*gpu_control_, GenerateFenceSyncRelease())
+      .WillOnce(Return(kFenceSync));
+  EXPECT_CALL(*gpu_control_, EnsureWorkVisible());
+  gpu::SyncToken sync_token;
+  gl_->GenSyncTokenCHROMIUM(sync_token.GetData());
+
+  int signaled_count = 0;
+
+  // Request a signal sync token, which gives a callback to the GpuControl to
+  // run when the sync token is reached.
+  base::OnceClosure signal_closure;
+  EXPECT_CALL(*gpu_control_, DoSignalSyncToken(_, _))
+      .WillOnce(Invoke([&signal_closure](const SyncToken& sync_token,
+                                         base::OnceClosure* callback) {
+        signal_closure = std::move(*callback);
+      }));
+  gl_->SignalSyncToken(sync_token,
+                       base::BindOnce(&CountCallback, &signaled_count));
+  EXPECT_EQ(0, signaled_count);
+
+  // Inform the RasterImplementation that the context is lost.
+  GpuControlClient* gl_as_client = gl_;
+  gl_as_client->OnGpuControlLostContext();
+
+  // When GpuControl runs the callback, the original callback we gave to
+  // RasterImplementation is *not* run, since the context is lost and we
+  // have already run the lost context callback.
+  std::move(signal_closure).Run();
+  EXPECT_EQ(0, signaled_count);
+}
+
+TEST_F(RasterImplementationTest, ReportLoss) {
+  GpuControlClient* gl_as_client = gl_;
+  int lost_count = 0;
+  gl_->SetLostContextCallback(base::BindOnce(&CountCallback, &lost_count));
+  EXPECT_EQ(0, lost_count);
+
+  EXPECT_EQ(static_cast<GLenum>(GL_NO_ERROR), gl_->GetGraphicsResetStatusKHR());
+  gl_as_client->OnGpuControlLostContext();
+  EXPECT_NE(static_cast<GLenum>(GL_NO_ERROR), gl_->GetGraphicsResetStatusKHR());
+  // The lost context callback should be run when RasterImplementation is
+  // notified of the loss.
+  EXPECT_EQ(1, lost_count);
+}
+
+TEST_F(RasterImplementationTest, ReportLossReentrant) {
+  GpuControlClient* gl_as_client = gl_;
+  int lost_count = 0;
+  gl_->SetLostContextCallback(base::BindOnce(&CountCallback, &lost_count));
+  EXPECT_EQ(0, lost_count);
+
+  EXPECT_EQ(static_cast<GLenum>(GL_NO_ERROR), gl_->GetGraphicsResetStatusKHR());
+  gl_as_client->OnGpuControlLostContextMaybeReentrant();
+  EXPECT_NE(static_cast<GLenum>(GL_NO_ERROR), gl_->GetGraphicsResetStatusKHR());
+  // The lost context callback should not be run yet to avoid calling back into
+  // clients re-entrantly, and having them re-enter RasterImplementation.
+  EXPECT_EQ(0, lost_count);
+}
+
+TEST_F(RasterImplementationManualInitTest, FailInitOnTransferBufferFail) {
+  ContextInitOptions init_options;
+  init_options.transfer_buffer_initialize_fail = true;
+  EXPECT_FALSE(Initialize(init_options));
+}
+
+TEST_F(RasterImplementationTest, DiscardableMemoryDelete) {
+  const GLuint texture_id = 1;
+  EXPECT_FALSE(discardable_texture_manager()->TextureIsValid(texture_id));
+  gl_->InitializeDiscardableTextureCHROMIUM(texture_id);
+  EXPECT_TRUE(discardable_texture_manager()->TextureIsValid(texture_id));
+
+  // Deleting a texture should clear its discardable entry.
+  gl_->DeleteTextures(1, &texture_id);
+  EXPECT_FALSE(discardable_texture_manager()->TextureIsValid(texture_id));
+}
+
+TEST_F(RasterImplementationTest, DiscardableTextureLockFail) {
+  const GLuint texture_id = 1;
+  gl_->InitializeDiscardableTextureCHROMIUM(texture_id);
+  EXPECT_TRUE(discardable_texture_manager()->TextureIsValid(texture_id));
+
+  // Unlock the handle on the client side.
+  gl_->UnlockDiscardableTextureCHROMIUM(texture_id);
+
+  // Unlock and delete the handle on the service side.
+  ClientDiscardableHandle client_handle =
+      discardable_texture_manager()->GetHandleForTesting(texture_id);
+  ServiceDiscardableHandle service_handle(client_handle.BufferForTesting(),
+                                          client_handle.byte_offset(),
+                                          client_handle.shm_id());
+  service_handle.Unlock();
+  EXPECT_TRUE(service_handle.Delete());
+
+  // Trying to re-lock the texture via GL should fail and delete the entry.
+  EXPECT_FALSE(gl_->LockDiscardableTextureCHROMIUM(texture_id));
+  EXPECT_FALSE(discardable_texture_manager()->TextureIsValid(texture_id));
+}
+
+TEST_F(RasterImplementationTest, DiscardableTextureDoubleInitError) {
+  const GLuint texture_id = 1;
+  gl_->InitializeDiscardableTextureCHROMIUM(texture_id);
+  EXPECT_EQ(GL_NO_ERROR, CheckError());
+  gl_->InitializeDiscardableTextureCHROMIUM(texture_id);
+  EXPECT_EQ(GL_INVALID_VALUE, CheckError());
+}
+
+TEST_F(RasterImplementationTest, DiscardableTextureLockError) {
+  const GLuint texture_id = 1;
+  EXPECT_FALSE(gl_->LockDiscardableTextureCHROMIUM(texture_id));
+  EXPECT_EQ(GL_INVALID_VALUE, CheckError());
+}
+
+TEST_F(RasterImplementationTest, DiscardableTextureLockCounting) {
+  const GLint texture_id = 1;
+  gl_->InitializeDiscardableTextureCHROMIUM(texture_id);
+  EXPECT_TRUE(discardable_texture_manager()->TextureIsValid(texture_id));
+
+  // Bind the texture.
+  gl_->BindTexture(GL_TEXTURE_2D, texture_id);
+  GLint bound_texture_id = 0;
+  gl_->GetIntegerv(GL_TEXTURE_BINDING_2D, &bound_texture_id);
+  EXPECT_EQ(texture_id, bound_texture_id);
+
+  // Lock the texture 3 more times (for 4 locks total).
+  for (int i = 0; i < 3; ++i) {
+    gl_->LockDiscardableTextureCHROMIUM(texture_id);
+  }
+
+  // Unlock 4 times. Only after the last unlock should the texture be unbound.
+  for (int i = 0; i < 4; ++i) {
+    gl_->UnlockDiscardableTextureCHROMIUM(texture_id);
+    bound_texture_id = 0;
+    gl_->GetIntegerv(GL_TEXTURE_BINDING_2D, &bound_texture_id);
+    if (i < 3) {
+      EXPECT_EQ(texture_id, bound_texture_id);
+    } else {
+      EXPECT_EQ(0, bound_texture_id);
+    }
+  }
+}
+
+#include "base/macros.h"
+#include "gpu/command_buffer/client/raster_implementation_unittest_autogen.h"
+
+}  // namespace raster
+}  // namespace gpu
diff --git a/gpu/command_buffer/client/raster_implementation_unittest_autogen.h b/gpu/command_buffer/client/raster_implementation_unittest_autogen.h
new file mode 100644
index 0000000..345a7d5
--- /dev/null
+++ b/gpu/command_buffer/client/raster_implementation_unittest_autogen.h
@@ -0,0 +1,135 @@
+// 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.
+
+// This file is auto-generated from
+// gpu/command_buffer/build_raster_cmd_buffer.py
+// It's formatted by clang-format using chromium coding style:
+//    clang-format -i -style=chromium filename
+// DO NOT EDIT!
+
+// This file is included by raster_implementation.h to declare the
+// GL api functions.
+#ifndef GPU_COMMAND_BUFFER_CLIENT_RASTER_IMPLEMENTATION_UNITTEST_AUTOGEN_H_
+#define GPU_COMMAND_BUFFER_CLIENT_RASTER_IMPLEMENTATION_UNITTEST_AUTOGEN_H_
+
+TEST_F(RasterImplementationTest, DeleteTextures) {
+  GLuint ids[2] = {kTexturesStartId, kTexturesStartId + 1};
+  struct Cmds {
+    cmds::DeleteTexturesImmediate del;
+    GLuint data[2];
+  };
+  Cmds expected;
+  expected.del.Init(arraysize(ids), &ids[0]);
+  expected.data[0] = kTexturesStartId;
+  expected.data[1] = kTexturesStartId + 1;
+  gl_->DeleteTextures(arraysize(ids), &ids[0]);
+  EXPECT_EQ(0, memcmp(&expected, commands_, sizeof(expected)));
+}
+
+TEST_F(RasterImplementationTest, Flush) {
+  struct Cmds {
+    cmds::Flush cmd;
+  };
+  Cmds expected;
+  expected.cmd.Init();
+
+  gl_->Flush();
+  EXPECT_EQ(0, memcmp(&expected, commands_, sizeof(expected)));
+}
+
+TEST_F(RasterImplementationTest, GetIntegerv) {
+  struct Cmds {
+    cmds::GetIntegerv cmd;
+  };
+  typedef cmds::GetIntegerv::Result::Type ResultType;
+  ResultType result = 0;
+  Cmds expected;
+  ExpectedMemoryInfo result1 =
+      GetExpectedResultMemory(sizeof(uint32_t) + sizeof(ResultType));
+  expected.cmd.Init(123, result1.id, result1.offset);
+  EXPECT_CALL(*command_buffer(), OnFlush())
+      .WillOnce(SetMemory(result1.ptr, SizedResultHelper<ResultType>(1)))
+      .RetiresOnSaturation();
+  gl_->GetIntegerv(123, &result);
+  EXPECT_EQ(0, memcmp(&expected, commands_, sizeof(expected)));
+  EXPECT_EQ(static_cast<ResultType>(1), result);
+}
+
+TEST_F(RasterImplementationTest, TexParameteri) {
+  struct Cmds {
+    cmds::TexParameteri cmd;
+  };
+  Cmds expected;
+  expected.cmd.Init(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
+
+  gl_->TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
+  EXPECT_EQ(0, memcmp(&expected, commands_, sizeof(expected)));
+}
+
+TEST_F(RasterImplementationTest, GenQueriesEXT) {
+  GLuint ids[2] = {
+      0,
+  };
+  struct Cmds {
+    cmds::GenQueriesEXTImmediate gen;
+    GLuint data[2];
+  };
+  Cmds expected;
+  expected.gen.Init(arraysize(ids), &ids[0]);
+  expected.data[0] = kQueriesStartId;
+  expected.data[1] = kQueriesStartId + 1;
+  gl_->GenQueriesEXT(arraysize(ids), &ids[0]);
+  EXPECT_EQ(0, memcmp(&expected, commands_, sizeof(expected)));
+  EXPECT_EQ(kQueriesStartId, ids[0]);
+  EXPECT_EQ(kQueriesStartId + 1, ids[1]);
+}
+
+TEST_F(RasterImplementationTest, DeleteQueriesEXT) {
+  GLuint ids[2] = {kQueriesStartId, kQueriesStartId + 1};
+  struct Cmds {
+    cmds::DeleteQueriesEXTImmediate del;
+    GLuint data[2];
+  };
+  Cmds expected;
+  expected.del.Init(arraysize(ids), &ids[0]);
+  expected.data[0] = kQueriesStartId;
+  expected.data[1] = kQueriesStartId + 1;
+  gl_->DeleteQueriesEXT(arraysize(ids), &ids[0]);
+  EXPECT_EQ(0, memcmp(&expected, commands_, sizeof(expected)));
+}
+
+TEST_F(RasterImplementationTest, CompressedCopyTextureCHROMIUM) {
+  struct Cmds {
+    cmds::CompressedCopyTextureCHROMIUM cmd;
+  };
+  Cmds expected;
+  expected.cmd.Init(1, 2);
+
+  gl_->CompressedCopyTextureCHROMIUM(1, 2);
+  EXPECT_EQ(0, memcmp(&expected, commands_, sizeof(expected)));
+}
+
+TEST_F(RasterImplementationTest, LoseContextCHROMIUM) {
+  struct Cmds {
+    cmds::LoseContextCHROMIUM cmd;
+  };
+  Cmds expected;
+  expected.cmd.Init(GL_GUILTY_CONTEXT_RESET_ARB, GL_GUILTY_CONTEXT_RESET_ARB);
+
+  gl_->LoseContextCHROMIUM(GL_GUILTY_CONTEXT_RESET_ARB,
+                           GL_GUILTY_CONTEXT_RESET_ARB);
+  EXPECT_EQ(0, memcmp(&expected, commands_, sizeof(expected)));
+}
+
+TEST_F(RasterImplementationTest, EndRasterCHROMIUM) {
+  struct Cmds {
+    cmds::EndRasterCHROMIUM cmd;
+  };
+  Cmds expected;
+  expected.cmd.Init();
+
+  gl_->EndRasterCHROMIUM();
+  EXPECT_EQ(0, memcmp(&expected, commands_, sizeof(expected)));
+}
+#endif  // GPU_COMMAND_BUFFER_CLIENT_RASTER_IMPLEMENTATION_UNITTEST_AUTOGEN_H_
diff --git a/gpu/command_buffer/service/gl_utils.cc b/gpu/command_buffer/service/gl_utils.cc
index 468e541..5b9e33c 100644
--- a/gpu/command_buffer/service/gl_utils.cc
+++ b/gpu/command_buffer/service/gl_utils.cc
@@ -265,15 +265,14 @@
     return "OpenGL ES GLSL ES 1.0 Chromium";
 }
 
-static void APIENTRY LogGLDebugMessage(GLenum source,
-                                       GLenum type,
-                                       GLuint id,
-                                       GLenum severity,
-                                       GLsizei length,
-                                       const GLchar* message,
-                                       GLvoid* user_param) {
+void LogGLDebugMessage(GLenum source,
+                       GLenum type,
+                       GLuint id,
+                       GLenum severity,
+                       GLsizei length,
+                       const GLchar* message,
+                       Logger* error_logger) {
   std::string id_string = GLES2Util::GetStringEnum(id);
-  Logger* error_logger = static_cast<Logger*>(user_param);
   if (type == GL_DEBUG_TYPE_ERROR && source == GL_DEBUG_SOURCE_API) {
     error_logger->LogMessage(__FILE__, __LINE__,
                              " " + id_string + ": " + message);
@@ -286,7 +285,9 @@
   }
 }
 
-void InitializeGLDebugLogging(bool log_non_errors, Logger* error_logger) {
+void InitializeGLDebugLogging(bool log_non_errors,
+                              GLDEBUGPROC callback,
+                              const void* user_param) {
   glEnable(GL_DEBUG_OUTPUT);
   glEnable(GL_DEBUG_OUTPUT_SYNCHRONOUS);
 
@@ -305,7 +306,7 @@
                           GL_DEBUG_SEVERITY_NOTIFICATION, 0, nullptr, GL_FALSE);
   }
 
-  glDebugMessageCallback(&LogGLDebugMessage, error_logger);
+  glDebugMessageCallback(callback, user_param);
 }
 
 bool ValidContextLostReason(GLenum reason) {
diff --git a/gpu/command_buffer/service/gl_utils.h b/gpu/command_buffer/service/gl_utils.h
index 19d9b01..34da7e7 100644
--- a/gpu/command_buffer/service/gl_utils.h
+++ b/gpu/command_buffer/service/gl_utils.h
@@ -80,7 +80,16 @@
 const char* GetServiceShadingLanguageVersionString(
     const FeatureInfo* feature_info);
 
-void InitializeGLDebugLogging(bool log_non_errors, Logger* error_logger);
+void LogGLDebugMessage(GLenum source,
+                       GLenum type,
+                       GLuint id,
+                       GLenum severity,
+                       GLsizei length,
+                       const GLchar* message,
+                       Logger* error_logger);
+void InitializeGLDebugLogging(bool log_non_errors,
+                              GLDEBUGPROC callback,
+                              const void* user_param);
 
 bool ValidContextLostReason(GLenum reason);
 error::ContextLostReason GetContextLostReasonFromResetStatus(
diff --git a/gpu/command_buffer/service/gles2_cmd_decoder.cc b/gpu/command_buffer/service/gles2_cmd_decoder.cc
index 0c587e8..e53846f 100644
--- a/gpu/command_buffer/service/gles2_cmd_decoder.cc
+++ b/gpu/command_buffer/service/gles2_cmd_decoder.cc
@@ -209,6 +209,17 @@
 
 void EmptyPresentation(const gfx::PresentationFeedback&) {}
 
+void APIENTRY GLDebugMessageCallback(GLenum source,
+                                     GLenum type,
+                                     GLuint id,
+                                     GLenum severity,
+                                     GLsizei length,
+                                     const GLchar* message,
+                                     GLvoid* user_param) {
+  Logger* error_logger = static_cast<Logger*>(user_param);
+  LogGLDebugMessage(source, type, id, severity, length, message, error_logger);
+}
+
 }  // namespace
 
 class GLES2DecoderImpl;
@@ -3837,7 +3848,7 @@
 
   if (group_->gpu_preferences().enable_gpu_driver_debug_logging &&
       feature_info_->feature_flags().khr_debug) {
-    InitializeGLDebugLogging(true, &logger_);
+    InitializeGLDebugLogging(true, GLDebugMessageCallback, &logger_);
   }
 
   if (feature_info_->feature_flags().chromium_texture_filtering_hint &&
diff --git a/gpu/command_buffer/service/gles2_cmd_decoder_passthrough.cc b/gpu/command_buffer/service/gles2_cmd_decoder_passthrough.cc
index d55dda440..0e877c7 100644
--- a/gpu/command_buffer/service/gles2_cmd_decoder_passthrough.cc
+++ b/gpu/command_buffer/service/gles2_cmd_decoder_passthrough.cc
@@ -76,6 +76,21 @@
   }
 }
 
+void APIENTRY GLDebugMessageCallback(GLenum source,
+                                     GLenum type,
+                                     GLuint id,
+                                     GLenum severity,
+                                     GLsizei length,
+                                     const GLchar* message,
+                                     GLvoid* user_param) {
+  DCHECK(user_param != nullptr);
+  GLES2DecoderPassthroughImpl* command_decoder =
+      static_cast<GLES2DecoderPassthroughImpl*>(const_cast<void*>(user_param));
+  command_decoder->OnDebugMessage(source, type, id, severity, length, message);
+  LogGLDebugMessage(source, type, id, severity, length, message,
+                    command_decoder->GetLogger());
+}
+
 }  // anonymous namespace
 
 PassthroughResources::PassthroughResources() : texture_object_map(nullptr) {}
@@ -601,8 +616,11 @@
         gl::GetRequestableGLExtensionsFromCurrentContext());
 
     static constexpr const char* kRequiredFunctionalityExtensions[] = {
-        "GL_CHROMIUM_bind_uniform_location", "GL_CHROMIUM_sync_query",
-        "GL_EXT_debug_marker", "GL_NV_fence",
+        "GL_CHROMIUM_bind_uniform_location",
+        "GL_CHROMIUM_sync_query",
+        "GL_EXT_debug_marker",
+        "GL_KHR_debug",
+        "GL_NV_fence",
     };
     RequestExtensions(api(), requestable_extensions,
                       kRequiredFunctionalityExtensions,
@@ -671,7 +689,8 @@
       api()->glIsEnabledFn(GL_CLIENT_ARRAYS_ANGLE) != GL_FALSE ||
       feature_info_->feature_flags().angle_webgl_compatibility !=
           IsWebGLContextType(attrib_helper.context_type) ||
-      !feature_info_->feature_flags().angle_request_extension) {
+      !feature_info_->feature_flags().angle_request_extension ||
+      !feature_info_->feature_flags().khr_debug) {
     Destroy(true);
     LOG(ERROR) << "ContextResult::kFatalFailure: "
                   "missing required extension";
@@ -726,15 +745,11 @@
     bound_buffers_[GL_DISPATCH_INDIRECT_BUFFER] = 0;
   }
 
-  if (feature_info_->feature_flags().khr_debug) {
-    // For WebGL contexts, log GL errors so they appear in devtools. Otherwise
-    // only enable debug logging if requested.
-    bool log_non_errors =
-        group_->gpu_preferences().enable_gpu_driver_debug_logging;
-    if (IsWebGLContextType(attrib_helper.context_type) || log_non_errors) {
-      InitializeGLDebugLogging(log_non_errors, &logger_);
-    }
-  }
+  // For WebGL contexts, log GL errors so they appear in devtools. Otherwise
+  // only enable debug logging if requested.
+  bool log_non_errors =
+      group_->gpu_preferences().enable_gpu_driver_debug_logging;
+  InitializeGLDebugLogging(log_non_errors, GLDebugMessageCallback, this);
 
   if (feature_info_->feature_flags().chromium_texture_filtering_hint &&
       feature_info_->feature_flags().is_swiftshader) {
@@ -801,7 +816,7 @@
       }
     }
 
-    FlushErrors();
+    CheckErrorCallbackState();
     emulated_back_buffer_ = std::make_unique<EmulatedDefaultFramebuffer>(
         api(), emulated_default_framebuffer_format_, feature_info_.get());
     // Make sure to use a non-empty offscreen surface so that the framebuffer is
@@ -819,7 +834,7 @@
                       : gpu::ContextResult::kFatalFailure;
     }
 
-    if (FlushErrors()) {
+    if (CheckErrorCallbackState()) {
       Destroy(true);
       // Errors are considered fatal, including OOM.
       LOG(ERROR)
@@ -1037,7 +1052,7 @@
     return false;
   }
 
-  FlushErrors();
+  CheckErrorCallbackState();
 
   if (!emulated_back_buffer_->Resize(size, feature_info_.get())) {
     LOG(ERROR) << "GLES2DecoderPassthroughImpl::ResizeOffscreenFramebuffer "
@@ -1045,7 +1060,7 @@
     return false;
   }
 
-  if (FlushErrors()) {
+  if (CheckErrorCallbackState()) {
     LOG(ERROR) << "GLES2DecoderPassthroughImpl::ResizeOffscreenFramebuffer "
                   "failed to resize the emulated framebuffer because errors "
                   "were generated.";
@@ -1463,6 +1478,17 @@
   passthrough_texture->SetLevelImage(texture_target, 0, image);
 }
 
+void GLES2DecoderPassthroughImpl::OnDebugMessage(GLenum source,
+                                                 GLenum type,
+                                                 GLuint id,
+                                                 GLenum severity,
+                                                 GLsizei length,
+                                                 const GLchar* message) {
+  if (type == GL_DEBUG_TYPE_ERROR && source == GL_DEBUG_SOURCE_API) {
+    had_error_callback_ = true;
+  }
+}
+
 const char* GLES2DecoderPassthroughImpl::GetCommandName(
     unsigned int command_id) const {
   if (command_id >= kFirstGLES2Command && command_id < kNumCommands) {
@@ -1690,19 +1716,8 @@
 }
 
 bool GLES2DecoderPassthroughImpl::FlushErrors() {
-  auto get_next_error = [this]() {
-    // Always read a real GL error so that it can be replaced by the injected
-    // error
-    GLenum error = api()->glGetErrorFn();
-    if (!injected_driver_errors_.empty()) {
-      error = injected_driver_errors_.front();
-      injected_driver_errors_.pop_front();
-    }
-    return error;
-  };
-
   bool had_error = false;
-  GLenum error = get_next_error();
+  GLenum error = glGetError();
   while (error != GL_NO_ERROR) {
     errors_.insert(error);
     had_error = true;
@@ -1721,15 +1736,11 @@
       break;
     }
 
-    error = get_next_error();
+    error = glGetError();
   }
   return had_error;
 }
 
-void GLES2DecoderPassthroughImpl::InjectDriverError(GLenum error) {
-  injected_driver_errors_.push_back(error);
-}
-
 bool GLES2DecoderPassthroughImpl::CheckResetStatus() {
   DCHECK(!WasContextLost());
   DCHECK(context_->IsCurrent(nullptr));
@@ -2059,6 +2070,16 @@
   }
 }
 
+bool GLES2DecoderPassthroughImpl::CheckErrorCallbackState() {
+  bool had_error_ = had_error_callback_;
+  had_error_callback_ = false;
+  if (had_error_) {
+    // Make sure lose-context-on-OOM logic is triggered as early as possible.
+    FlushErrors();
+  }
+  return had_error_;
+}
+
 #define GLES2_CMD_OP(name)                                               \
   {                                                                      \
       &GLES2DecoderPassthroughImpl::Handle##name, cmds::name::kArgFlags, \
diff --git a/gpu/command_buffer/service/gles2_cmd_decoder_passthrough.h b/gpu/command_buffer/service/gles2_cmd_decoder_passthrough.h
index 8c52069c..716080c 100644
--- a/gpu/command_buffer/service/gles2_cmd_decoder_passthrough.h
+++ b/gpu/command_buffer/service/gles2_cmd_decoder_passthrough.h
@@ -305,6 +305,13 @@
                  gl::GLImage* image,
                  bool can_bind_to_sampler) override;
 
+  void OnDebugMessage(GLenum source,
+                      GLenum type,
+                      GLuint id,
+                      GLenum severity,
+                      GLsizei length,
+                      const GLchar* message);
+
  private:
   // Allow unittests to inspect internal state tracking
   friend class GLES2DecoderPassthroughTestBase;
@@ -363,10 +370,6 @@
   GLenum PopError();
   bool FlushErrors();
 
-  // Inject a driver-level GL error that will replace the result of the next
-  // call to glGetError
-  void InjectDriverError(GLenum error);
-
   bool IsRobustnessSupported();
 
   bool IsEmulatedQueryTarget(GLenum target) const;
@@ -559,9 +562,13 @@
   base::circular_deque<PendingReadPixels> pending_read_pixels_;
 
   // Error state
-  base::circular_deque<GLenum> injected_driver_errors_;
   std::set<GLenum> errors_;
 
+  // Checks if an error has been generated since the last call to
+  // CheckErrorCallbackState
+  bool CheckErrorCallbackState();
+  bool had_error_callback_ = false;
+
   // Default framebuffer emulation
   struct EmulatedDefaultFramebufferFormat {
     GLenum color_renderbuffer_internal_format = GL_NONE;
diff --git a/gpu/command_buffer/service/gles2_cmd_decoder_passthrough_doers.cc b/gpu/command_buffer/service/gles2_cmd_decoder_passthrough_doers.cc
index 551ddc6c..08bf441 100644
--- a/gpu/command_buffer/service/gles2_cmd_decoder_passthrough_doers.cc
+++ b/gpu/command_buffer/service/gles2_cmd_decoder_passthrough_doers.cc
@@ -320,9 +320,9 @@
 
 // Implementations of commands
 error::Error GLES2DecoderPassthroughImpl::DoActiveTexture(GLenum texture) {
-  FlushErrors();
+  CheckErrorCallbackState();
   api()->glActiveTextureFn(texture);
-  if (FlushErrors()) {
+  if (CheckErrorCallbackState()) {
     return error::kNoError;
   }
 
@@ -350,10 +350,10 @@
 
 error::Error GLES2DecoderPassthroughImpl::DoBindBuffer(GLenum target,
                                                        GLuint buffer) {
-  FlushErrors();
+  CheckErrorCallbackState();
   api()->glBindBufferFn(target, GetBufferServiceID(api(), buffer, resources_,
                                                    bind_generates_resource_));
-  if (FlushErrors()) {
+  if (CheckErrorCallbackState()) {
     return error::kNoError;
   }
 
@@ -366,11 +366,11 @@
 error::Error GLES2DecoderPassthroughImpl::DoBindBufferBase(GLenum target,
                                                            GLuint index,
                                                            GLuint buffer) {
-  FlushErrors();
+  CheckErrorCallbackState();
   api()->glBindBufferBaseFn(
       target, index,
       GetBufferServiceID(api(), buffer, resources_, bind_generates_resource_));
-  if (FlushErrors()) {
+  if (CheckErrorCallbackState()) {
     return error::kNoError;
   }
 
@@ -385,12 +385,12 @@
                                                             GLuint buffer,
                                                             GLintptr offset,
                                                             GLsizeiptr size) {
-  FlushErrors();
+  CheckErrorCallbackState();
   api()->glBindBufferRangeFn(
       target, index,
       GetBufferServiceID(api(), buffer, resources_, bind_generates_resource_),
       offset, size);
-  if (FlushErrors()) {
+  if (CheckErrorCallbackState()) {
     return error::kNoError;
   }
 
@@ -403,11 +403,11 @@
 error::Error GLES2DecoderPassthroughImpl::DoBindFramebuffer(
     GLenum target,
     GLuint framebuffer) {
-  FlushErrors();
+  CheckErrorCallbackState();
   api()->glBindFramebufferEXTFn(
       target, GetFramebufferServiceID(api(), framebuffer, &framebuffer_id_map_,
                                       bind_generates_resource_));
-  if (FlushErrors()) {
+  if (CheckErrorCallbackState()) {
     return error::kNoError;
   }
 
@@ -454,12 +454,12 @@
   GLuint service_id =
       GetTextureServiceID(api(), texture, resources_, bind_generates_resource_);
 
-  FlushErrors();
+  CheckErrorCallbackState();
 
   api()->glBindTextureFn(target, service_id);
 
   // Only update tracking if no error was generated in the bind call
-  if (FlushErrors()) {
+  if (CheckErrorCallbackState()) {
     return error::kNoError;
   }
 
@@ -536,9 +536,9 @@
                                                        GLsizeiptr size,
                                                        const void* data,
                                                        GLenum usage) {
-  FlushErrors();
+  CheckErrorCallbackState();
   api()->glBufferDataFn(target, size, data, usage);
-  if (FlushErrors()) {
+  if (CheckErrorCallbackState()) {
     return error::kNoError;
   }
 
@@ -1051,10 +1051,10 @@
     return error::kInvalidArguments;
   }
 
-  FlushErrors();
+  CheckErrorCallbackState();
   GLsync service_id = api()->glFenceSyncFn(condition, flags);
-  if (FlushErrors()) {
-    return error::kInvalidArguments;
+  if (CheckErrorCallbackState()) {
+    return error::kNoError;
   }
 
   resources_->sync_id_map.SetIDMapping(client_id,
@@ -1254,13 +1254,13 @@
                                                             GLenum* type,
                                                             std::string* name,
                                                             int32_t* success) {
-  FlushErrors();
+  CheckErrorCallbackState();
 
   GLuint service_id = GetProgramServiceID(program, resources_);
   GLint active_attribute_max_length = 0;
   api()->glGetProgramivFn(service_id, GL_ACTIVE_ATTRIBUTE_MAX_LENGTH,
                           &active_attribute_max_length);
-  if (FlushErrors()) {
+  if (CheckErrorCallbackState()) {
     *success = 0;
     return error::kNoError;
   }
@@ -1269,7 +1269,7 @@
   api()->glGetActiveAttribFn(service_id, index, name_buffer.size(), nullptr,
                              size, type, name_buffer.data());
   *name = std::string(name_buffer.data());
-  *success = FlushErrors() ? 0 : 1;
+  *success = CheckErrorCallbackState() ? 0 : 1;
   return error::kNoError;
 }
 
@@ -1279,13 +1279,13 @@
                                                              GLenum* type,
                                                              std::string* name,
                                                              int32_t* success) {
-  FlushErrors();
+  CheckErrorCallbackState();
 
   GLuint service_id = GetProgramServiceID(program, resources_);
   GLint active_uniform_max_length = 0;
   api()->glGetProgramivFn(service_id, GL_ACTIVE_UNIFORM_MAX_LENGTH,
                           &active_uniform_max_length);
-  if (FlushErrors()) {
+  if (CheckErrorCallbackState()) {
     *success = 0;
     return error::kNoError;
   }
@@ -1294,7 +1294,7 @@
   api()->glGetActiveUniformFn(service_id, index, name_buffer.size(), nullptr,
                               size, type, name_buffer.data());
   *name = std::string(name_buffer.data());
-  *success = FlushErrors() ? 0 : 1;
+  *success = CheckErrorCallbackState() ? 0 : 1;
   return error::kNoError;
 }
 
@@ -1315,7 +1315,7 @@
     GLuint program,
     GLuint index,
     std::string* name) {
-  FlushErrors();
+  CheckErrorCallbackState();
 
   GLuint program_service_id = GetProgramServiceID(program, resources_);
   GLint max_name_length = 0;
@@ -1323,7 +1323,7 @@
                           GL_ACTIVE_UNIFORM_BLOCK_MAX_NAME_LENGTH,
                           &max_name_length);
 
-  if (FlushErrors()) {
+  if (CheckErrorCallbackState()) {
     return error::kNoError;
   }
 
@@ -1371,11 +1371,11 @@
     GLintptr offset,
     GLsizeiptr size,
     uint8_t* mem) {
-  FlushErrors();
+  CheckErrorCallbackState();
 
   void* mapped_ptr =
       api()->glMapBufferRangeFn(target, offset, size, GL_MAP_READ_BIT);
-  if (FlushErrors() || mapped_ptr == nullptr) {
+  if (CheckErrorCallbackState() || mapped_ptr == nullptr) {
     // Had an error while mapping, don't copy any data
     return error::kNoError;
   }
@@ -1404,10 +1404,10 @@
     GLsizei bufsize,
     GLsizei* length,
     GLint64* params) {
-  FlushErrors();
+  CheckErrorCallbackState();
   api()->glGetBufferParameteri64vRobustANGLEFn(target, pname, bufsize, length,
                                                params);
-  if (FlushErrors()) {
+  if (CheckErrorCallbackState()) {
     return error::kNoError;
   }
   PatchGetBufferResults(target, pname, bufsize, length, params);
@@ -1420,10 +1420,10 @@
     GLsizei bufsize,
     GLsizei* length,
     GLint* params) {
-  FlushErrors();
+  CheckErrorCallbackState();
   api()->glGetBufferParameterivRobustANGLEFn(target, pname, bufsize, length,
                                              params);
-  if (FlushErrors()) {
+  if (CheckErrorCallbackState()) {
     return error::kNoError;
   }
   PatchGetBufferResults(target, pname, bufsize, length, params);
@@ -1485,14 +1485,14 @@
     }
   }
 
-  FlushErrors();
+  CheckErrorCallbackState();
 
   // Get a scratch buffer to hold the result of the query
   GLint* scratch_params = GetTypedScratchMemory<GLint>(bufsize);
   api()->glGetFramebufferAttachmentParameterivRobustANGLEFn(
       target, updated_attachment, pname, bufsize, length, scratch_params);
 
-  if (FlushErrors()) {
+  if (CheckErrorCallbackState()) {
     DCHECK(*length == 0);
     return error::kNoError;
   }
@@ -1576,12 +1576,12 @@
 error::Error GLES2DecoderPassthroughImpl::DoGetProgramInfoLog(
     GLuint program,
     std::string* infolog) {
-  FlushErrors();
+  CheckErrorCallbackState();
   GLint info_log_len = 0;
   api()->glGetProgramivFn(GetProgramServiceID(program, resources_),
                           GL_INFO_LOG_LENGTH, &info_log_len);
 
-  if (FlushErrors()) {
+  if (CheckErrorCallbackState()) {
     return error::kNoError;
   }
 
@@ -1640,12 +1640,12 @@
 error::Error GLES2DecoderPassthroughImpl::DoGetShaderInfoLog(
     GLuint shader,
     std::string* infolog) {
-  FlushErrors();
+  CheckErrorCallbackState();
 
   GLuint service_id = GetShaderServiceID(shader, resources_);
   GLint info_log_len = 0;
   api()->glGetShaderivFn(service_id, GL_INFO_LOG_LENGTH, &info_log_len);
-  if (FlushErrors()) {
+  if (CheckErrorCallbackState()) {
     return error::kNoError;
   }
 
@@ -1663,23 +1663,23 @@
     GLint* range,
     GLint* precision,
     int32_t* success) {
-  FlushErrors();
+  CheckErrorCallbackState();
   api()->glGetShaderPrecisionFormatFn(shadertype, precisiontype, range,
                                       precision);
-  *success = FlushErrors() ? 0 : 1;
+  *success = CheckErrorCallbackState() ? 0 : 1;
   return error::kNoError;
 }
 
 error::Error GLES2DecoderPassthroughImpl::DoGetShaderSource(
     GLuint shader,
     std::string* source) {
-  FlushErrors();
+  CheckErrorCallbackState();
 
   GLuint shader_service_id = GetShaderServiceID(shader, resources_);
   GLint shader_source_length = 0;
   api()->glGetShaderivFn(shader_service_id, GL_SHADER_SOURCE_LENGTH,
                          &shader_source_length);
-  if (FlushErrors()) {
+  if (CheckErrorCallbackState()) {
     return error::kNoError;
   }
 
@@ -1757,13 +1757,13 @@
     GLenum* type,
     std::string* name,
     int32_t* success) {
-  FlushErrors();
+  CheckErrorCallbackState();
 
   GLuint service_id = GetProgramServiceID(program, resources_);
   GLint transform_feedback_varying_max_length = 0;
   api()->glGetProgramivFn(service_id, GL_TRANSFORM_FEEDBACK_VARYING_MAX_LENGTH,
                           &transform_feedback_varying_max_length);
-  if (FlushErrors()) {
+  if (CheckErrorCallbackState()) {
     *success = 0;
     return error::kNoError;
   }
@@ -1773,7 +1773,7 @@
                                          nullptr, size, type,
                                          name_buffer.data());
   *name = std::string(name_buffer.data());
-  *success = FlushErrors() ? 0 : 1;
+  *success = CheckErrorCallbackState() ? 0 : 1;
   return error::kNoError;
 }
 
@@ -2077,12 +2077,12 @@
                                                        GLsizei* rows,
                                                        void* pixels,
                                                        int32_t* success) {
-  FlushErrors();
+  CheckErrorCallbackState();
   ScopedPackStateRowLengthReset reset_row_length(
       api(), bufsize != 0 && feature_info_->gl_version_info().is_es3);
   api()->glReadPixelsRobustANGLEFn(x, y, width, height, format, type, bufsize,
                                    length, columns, rows, pixels);
-  *success = FlushErrors() ? 0 : 1;
+  *success = CheckErrorCallbackState() ? 0 : 1;
   return error::kNoError;
 }
 
@@ -2104,7 +2104,7 @@
   DCHECK(feature_info_->feature_flags().use_async_readpixels &&
          bound_buffers_[GL_PIXEL_PACK_BUFFER] == 0);
 
-  FlushErrors();
+  CheckErrorCallbackState();
   ScopedPackStateRowLengthReset reset_row_length(
       api(), bufsize != 0 && feature_info_->gl_version_info().is_es3);
 
@@ -2151,13 +2151,13 @@
   api()->glBindBufferFn(GL_PIXEL_PACK_BUFFER_ARB, 0);
 
   // Test for errors now before creating a fence
-  if (FlushErrors()) {
+  if (CheckErrorCallbackState()) {
     return error::kNoError;
   }
 
   pending_read_pixels.fence.reset(gl::GLFence::Create());
 
-  if (FlushErrors()) {
+  if (CheckErrorCallbackState()) {
     return error::kNoError;
   }
 
@@ -3009,12 +3009,12 @@
   GLuint service_id = GetQueryServiceID(id, &query_id_map_);
 
   // Flush all previous errors
-  FlushErrors();
+  CheckErrorCallbackState();
 
   api()->glQueryCounterFn(service_id, target);
 
   // Check if a new error was generated
-  if (FlushErrors()) {
+  if (CheckErrorCallbackState()) {
     return error::kNoError;
   }
 
@@ -3070,12 +3070,12 @@
     }
   } else {
     // Flush all previous errors
-    FlushErrors();
+    CheckErrorCallbackState();
 
     api()->glBeginQueryFn(target, service_id);
 
     // Check if a new error was generated
-    if (FlushErrors()) {
+    if (CheckErrorCallbackState()) {
       return error::kNoError;
     }
   }
@@ -3117,12 +3117,12 @@
     }
   } else {
     // Flush all previous errors
-    FlushErrors();
+    CheckErrorCallbackState();
 
     api()->glEndQueryFn(target);
 
     // Check if a new error was generated
-    if (FlushErrors()) {
+    if (CheckErrorCallbackState()) {
       return error::kNoError;
     }
   }
@@ -3292,7 +3292,7 @@
     int32_t data_shm_id,
     uint32_t data_shm_offset,
     uint32_t* result) {
-  FlushErrors();
+  CheckErrorCallbackState();
 
   GLbitfield filtered_access = access;
 
@@ -3315,7 +3315,7 @@
 
   void* mapped_ptr =
       api()->glMapBufferRangeFn(target, offset, size, filtered_access);
-  if (FlushErrors() || mapped_ptr == nullptr) {
+  if (CheckErrorCallbackState() || mapped_ptr == nullptr) {
     // Had an error while mapping, don't copy any data
     *result = 0;
     return error::kNoError;
@@ -3811,12 +3811,12 @@
 error::Error GLES2DecoderPassthroughImpl::DoGetTranslatedShaderSourceANGLE(
     GLuint shader,
     std::string* source) {
-  FlushErrors();
+  CheckErrorCallbackState();
   GLuint service_id = GetShaderServiceID(shader, resources_);
   GLint translated_source_length = 0;
   api()->glGetShaderivFn(service_id, GL_TRANSLATED_SHADER_SOURCE_LENGTH_ANGLE,
                          &translated_source_length);
-  if (FlushErrors()) {
+  if (CheckErrorCallbackState()) {
     return error::kNoError;
   }
 
@@ -4590,8 +4590,6 @@
     GLint y,
     GLint width,
     GLint height) {
-  FlushErrors();
-
   GLint current_framebuffer = 0;
   api()->glGetIntegervFn(GL_FRAMEBUFFER_BINDING, &current_framebuffer);
   if (current_framebuffer != 0) {
@@ -4616,8 +4614,6 @@
 
 error::Error GLES2DecoderPassthroughImpl::DoSetEnableDCLayersCHROMIUM(
     GLboolean enable) {
-  FlushErrors();
-
   GLint current_framebuffer = 0;
   api()->glGetIntegervFn(GL_FRAMEBUFFER_BINDING, &current_framebuffer);
   if (current_framebuffer != 0) {
diff --git a/gpu/command_buffer/service/gles2_cmd_decoder_passthrough_unittest_framebuffers.cc b/gpu/command_buffer/service/gles2_cmd_decoder_passthrough_unittest_framebuffers.cc
index c8fd6c1..56d10fc 100644
--- a/gpu/command_buffer/service/gles2_cmd_decoder_passthrough_unittest_framebuffers.cc
+++ b/gpu/command_buffer/service/gles2_cmd_decoder_passthrough_unittest_framebuffers.cc
@@ -349,12 +349,10 @@
   uint32_t pixels_shm_id = shared_memory_id_;
   uint32_t pixels_shm_offset = kSharedMemoryOffset + sizeof(Result);
 
-  // Inject an INVALID_OPERATION error on the call to ReadPixels
-  InjectGLError(GL_NO_ERROR);
-  InjectGLError(GL_INVALID_OPERATION);
-
+  // Provide parameters that will cause glReadPixels to fail with
+  // GL_INVALID_OPERATION
   ReadPixels cmd;
-  cmd.Init(0, 0, kWidth, kHeight, GL_RGBA, GL_UNSIGNED_BYTE, pixels_shm_id,
+  cmd.Init(0, 0, kWidth, kHeight, GL_RGBA, GL_UNSIGNED_SHORT, pixels_shm_id,
            pixels_shm_offset, result_shm_id, result_shm_offset, true);
   result->success = 0;
   EXPECT_EQ(error::kNoError, ExecuteCmd(cmd));
diff --git a/gpu/command_buffer/service/gles2_cmd_decoder_unittest_base.cc b/gpu/command_buffer/service/gles2_cmd_decoder_unittest_base.cc
index 09ef3e7..365b482e 100644
--- a/gpu/command_buffer/service/gles2_cmd_decoder_unittest_base.cc
+++ b/gpu/command_buffer/service/gles2_cmd_decoder_unittest_base.cc
@@ -2452,10 +2452,6 @@
   return static_cast<GLint>(*GetSharedMemoryAs<GLenum*>());
 }
 
-void GLES2DecoderPassthroughTestBase::InjectGLError(GLenum error) {
-  decoder_->InjectDriverError(error);
-}
-
 void GLES2DecoderPassthroughTestBase::DoRequestExtension(
     const char* extension) {
   DCHECK(extension != nullptr);
diff --git a/gpu/command_buffer/service/gles2_cmd_decoder_unittest_base.h b/gpu/command_buffer/service/gles2_cmd_decoder_unittest_base.h
index d37d4e48..9bdcfb1b 100644
--- a/gpu/command_buffer/service/gles2_cmd_decoder_unittest_base.h
+++ b/gpu/command_buffer/service/gles2_cmd_decoder_unittest_base.h
@@ -921,7 +921,6 @@
   }
 
   GLint GetGLError();
-  void InjectGLError(GLenum error);
 
  protected:
   void DoRequestExtension(const char* extension);
diff --git a/gpu/ipc/service/gpu_init.cc b/gpu/ipc/service/gpu_init.cc
index 120b150b..02eb05c1d 100644
--- a/gpu/ipc/service/gpu_init.cc
+++ b/gpu/ipc/service/gpu_init.cc
@@ -19,7 +19,7 @@
 #include "gpu/config/gpu_util.h"
 #include "gpu/ipc/service/gpu_watchdog_thread.h"
 #include "gpu/ipc/service/switches.h"
-#include "ui/base/ui_base_switches_util.h"
+#include "ui/base/ui_base_features.h"
 #include "ui/gfx/switches.h"
 #include "ui/gl/gl_features.h"
 #include "ui/gl/gl_implementation.h"
@@ -328,8 +328,12 @@
 #if defined(USE_OZONE)
   ui::OzonePlatform::InitParams params;
   params.single_process = true;
-  params.using_mojo = switches::IsMusHostingViz() ||
+#if defined(OS_CHROMEOS)
+  params.using_mojo = base::FeatureList::IsEnabled(features::kMash) ||
                       command_line->HasSwitch(switches::kEnableDrmMojo);
+#else
+  params.using_mojo = command_line->HasSwitch(switches::kEnableDrmMojo);
+#endif
   ui::OzonePlatform::InitializeForGPU(params);
   ui::OzonePlatform::GetInstance()->AfterSandboxEntry();
 #endif
diff --git a/gpu/ipc/service/gpu_watchdog_thread.cc b/gpu/ipc/service/gpu_watchdog_thread.cc
index de3e362e..5d1f725 100644
--- a/gpu/ipc/service/gpu_watchdog_thread.cc
+++ b/gpu/ipc/service/gpu_watchdog_thread.cc
@@ -158,8 +158,11 @@
 #if defined(USE_X11)
   if (tty_file_)
     fclose(tty_file_);
-  XDestroyWindow(display_, window_);
-  XCloseDisplay(display_);
+  if (display_) {
+    DCHECK(window_);
+    XDestroyWindow(display_, window_);
+    XCloseDisplay(display_);
+  }
 #endif
 
   watched_message_loop_->RemoveTaskObserver(&task_observer_);
@@ -312,46 +315,48 @@
 #endif
 
 #if defined(USE_X11)
-  XWindowAttributes attributes;
-  XGetWindowAttributes(display_, window_, &attributes);
+  if (display_) {
+    DCHECK(window_);
+    XWindowAttributes attributes;
+    XGetWindowAttributes(display_, window_, &attributes);
 
-  XSelectInput(display_, window_, PropertyChangeMask);
-  SetupXChangeProp();
+    XSelectInput(display_, window_, PropertyChangeMask);
+    SetupXChangeProp();
 
-  XFlush(display_);
+    XFlush(display_);
 
-  // We wait for the property change event with a timeout. If it arrives we know
-  // that X is responsive and is not the cause of the watchdog trigger, so we
-  // should
-  // terminate. If it times out, it may be due to X taking a long time, but
-  // terminating won't help, so ignore the watchdog trigger.
-  XEvent event_return;
-  base::TimeTicks deadline = base::TimeTicks::Now() + timeout_;
-  while (true) {
-    base::TimeDelta delta = deadline - base::TimeTicks::Now();
-    if (delta < base::TimeDelta()) {
-      return;
-    } else {
-      while (XCheckWindowEvent(display_, window_, PropertyChangeMask,
-                               &event_return)) {
-        if (MatchXEventAtom(&event_return))
-          break;
-      }
-      struct pollfd fds[1];
-      fds[0].fd = XConnectionNumber(display_);
-      fds[0].events = POLLIN;
-      int status = poll(fds, 1, delta.InMilliseconds());
-      if (status == -1) {
-        if (errno == EINTR) {
-          continue;
-        } else {
-          LOG(FATAL) << "Lost X connection, aborting.";
-          break;
-        }
-      } else if (status == 0) {
+    // We wait for the property change event with a timeout. If it arrives we
+    // know that X is responsive and is not the cause of the watchdog trigger,
+    // so we should terminate. If it times out, it may be due to X taking a long
+    // time, but terminating won't help, so ignore the watchdog trigger.
+    XEvent event_return;
+    base::TimeTicks deadline = base::TimeTicks::Now() + timeout_;
+    while (true) {
+      base::TimeDelta delta = deadline - base::TimeTicks::Now();
+      if (delta < base::TimeDelta()) {
         return;
       } else {
-        continue;
+        while (XCheckWindowEvent(display_, window_, PropertyChangeMask,
+                                 &event_return)) {
+          if (MatchXEventAtom(&event_return))
+            break;
+        }
+        struct pollfd fds[1];
+        fds[0].fd = XConnectionNumber(display_);
+        fds[0].events = POLLIN;
+        int status = poll(fds, 1, delta.InMilliseconds());
+        if (status == -1) {
+          if (errno == EINTR) {
+            continue;
+          } else {
+            LOG(FATAL) << "Lost X connection, aborting.";
+            break;
+          }
+        } else if (status == 0) {
+          return;
+        } else {
+          continue;
+        }
       }
     }
   }
@@ -426,13 +431,17 @@
 #if defined(USE_X11)
 void GpuWatchdogThread::SetupXServer() {
   display_ = XOpenDisplay(NULL);
-  window_ = XCreateWindow(display_, DefaultRootWindow(display_), 0, 0, 1, 1, 0,
-                          CopyFromParent, InputOutput, CopyFromParent, 0, NULL);
-  atom_ = XInternAtom(display_, "CHECK", x11::False);
+  if (display_) {
+    window_ =
+        XCreateWindow(display_, DefaultRootWindow(display_), 0, 0, 1, 1, 0,
+                      CopyFromParent, InputOutput, CopyFromParent, 0, NULL);
+    atom_ = XInternAtom(display_, "CHECK", x11::False);
+  }
   host_tty_ = GetActiveTTY();
 }
 
 void GpuWatchdogThread::SetupXChangeProp() {
+  DCHECK(display_);
   XChangeProperty(display_, window_, atom_, XA_STRING, 8, PropModeReplace, text,
                   (arraysize(text) - 1));
 }
diff --git a/ios/build/bots/tests/eg_cq_tests.json b/ios/build/bots/tests/eg_cq_tests.json
index a8617456..a55a71b2 100644
--- a/ios/build/bots/tests/eg_cq_tests.json
+++ b/ios/build/bots/tests/eg_cq_tests.json
@@ -5,10 +5,12 @@
   "tests": [
     {
       "app": "ios_chrome_integration_egtests",
+      "shards": 2,
       "xctest": true
     },
     {
       "app": "ios_web_shell_egtests",
+      "shards": 2,
       "xctest": true
     }
   ]
diff --git a/ios/build/bots/tests/eg_tests.json b/ios/build/bots/tests/eg_tests.json
index 31f12d59..28d690c 100644
--- a/ios/build/bots/tests/eg_tests.json
+++ b/ios/build/bots/tests/eg_tests.json
@@ -5,26 +5,33 @@
   "tests": [
     {
       "app": "ios_chrome_ui_egtests",
+      "shard size": 8,
+      "shards": 2,
       "xctest": true
     },
     {
       "app": "ios_chrome_adaptive_toolbar_egtests",
+      "shards": 2,
       "xctest": true
     },
     {
       "app": "ios_chrome_bookmarks_egtests",
+      "shards": 2,
       "xctest": true
     },
     {
       "app": "ios_chrome_web_egtests",
+      "shards": 2,
       "xctest": true
     },
     {
       "app": "ios_chrome_settings_egtests",
+      "shards": 2,
       "xctest": true
     },
     {
       "app": "ios_chrome_payments_egtests",
+      "shards": 2,
       "test args": [
         "--enable-features=WebPayments"
       ],
@@ -32,6 +39,7 @@
     },
     {
       "app": "ios_chrome_reading_list_egtests",
+      "shards": 2,
       "test args": [
         "--enable-reading-list"
       ],
@@ -39,6 +47,7 @@
     },
     {
       "app": "ios_showcase_egtests",
+      "shards": 2,
       "xctest": true
     }
   ]
diff --git a/ios/chrome/DEPS b/ios/chrome/DEPS
index 6304058..93bec53 100644
--- a/ios/chrome/DEPS
+++ b/ios/chrome/DEPS
@@ -3,6 +3,7 @@
   "+ios/shared/chrome/common",
   "+ios/third_party",
   "+net",
+  "+services/network/test",
   "+sql",
   "+ui/base",
   "+ui/gfx",
diff --git a/ios/chrome/browser/OWNERS b/ios/chrome/browser/OWNERS
index d3a11cd3..7cbfc15 100644
--- a/ios/chrome/browser/OWNERS
+++ b/ios/chrome/browser/OWNERS
@@ -1,4 +1,3 @@
-per-file ios_chrome_io_thread*=cbentzel@chromium.org
 per-file ios_chrome_io_thread*=mmenke@chromium.org
 per-file ios_chrome_io_thread*=rch@chromium.org
 per-file ios_chrome_io_thread*=rsleevi@chromium.org
diff --git a/ios/chrome/browser/ssl/BUILD.gn b/ios/chrome/browser/ssl/BUILD.gn
index a9fcd7d..5dbe830 100644
--- a/ios/chrome/browser/ssl/BUILD.gn
+++ b/ios/chrome/browser/ssl/BUILD.gn
@@ -75,6 +75,7 @@
     "//ios/web/public/test",
     "//net",
     "//net:test_support",
+    "//services/network:test_support",
     "//third_party/ocmock",
   ]
 }
diff --git a/ios/chrome/browser/ssl/captive_portal_detector_tab_helper.h b/ios/chrome/browser/ssl/captive_portal_detector_tab_helper.h
index bb8fbd96..53a87e51 100644
--- a/ios/chrome/browser/ssl/captive_portal_detector_tab_helper.h
+++ b/ios/chrome/browser/ssl/captive_portal_detector_tab_helper.h
@@ -13,6 +13,12 @@
 class CaptivePortalDetector;
 }
 
+namespace network {
+namespace mojom {
+class URLLoaderFactory;
+}
+}  // namespace network
+
 namespace web {
 class WebState;
 }
@@ -27,7 +33,8 @@
   // retained by the CaptivePortalDetectorTabHelper and must not be nil.
   static void CreateForWebState(
       web::WebState* web_state,
-      id<CaptivePortalDetectorTabHelperDelegate> delegate);
+      id<CaptivePortalDetectorTabHelperDelegate> delegate,
+      network::mojom::URLLoaderFactory* loader_factory_for_testing = nullptr);
 
   // Returns the associated captive portal detector.
   captive_portal::CaptivePortalDetector* detector();
@@ -38,7 +45,8 @@
  private:
   CaptivePortalDetectorTabHelper(
       web::WebState* web_state,
-      id<CaptivePortalDetectorTabHelperDelegate> delegate);
+      id<CaptivePortalDetectorTabHelperDelegate> delegate,
+      network::mojom::URLLoaderFactory* loader_factory_for_testing);
 
   // The delegate to notify when the user performs an action in response to the
   // captive portal detector state.
diff --git a/ios/chrome/browser/ssl/captive_portal_detector_tab_helper.mm b/ios/chrome/browser/ssl/captive_portal_detector_tab_helper.mm
index cdf5b83c..ddf48ec54 100644
--- a/ios/chrome/browser/ssl/captive_portal_detector_tab_helper.mm
+++ b/ios/chrome/browser/ssl/captive_portal_detector_tab_helper.mm
@@ -21,21 +21,25 @@
 // static
 void CaptivePortalDetectorTabHelper::CreateForWebState(
     web::WebState* web_state,
-    id<CaptivePortalDetectorTabHelperDelegate> delegate) {
+    id<CaptivePortalDetectorTabHelperDelegate> delegate,
+    network::mojom::URLLoaderFactory* loader_factory_for_testing) {
   DCHECK(web_state);
   if (!FromWebState(web_state)) {
-    web_state->SetUserData(UserDataKey(),
-                           base::WrapUnique(new CaptivePortalDetectorTabHelper(
-                               web_state, delegate)));
+    web_state->SetUserData(
+        UserDataKey(), base::WrapUnique(new CaptivePortalDetectorTabHelper(
+                           web_state, delegate, loader_factory_for_testing)));
   }
 }
 
 CaptivePortalDetectorTabHelper::CaptivePortalDetectorTabHelper(
     web::WebState* web_state,
-    id<CaptivePortalDetectorTabHelperDelegate> delegate)
+    id<CaptivePortalDetectorTabHelperDelegate> delegate,
+    network::mojom::URLLoaderFactory* loader_factory_for_testing)
     : delegate_(delegate),
       detector_(std::make_unique<captive_portal::CaptivePortalDetector>(
-          web_state->GetBrowserState()->GetURLLoaderFactory())) {
+          loader_factory_for_testing
+              ? loader_factory_for_testing
+              : web_state->GetBrowserState()->GetURLLoaderFactory())) {
   DCHECK(delegate);
 }
 
diff --git a/ios/chrome/browser/ssl/ios_ssl_error_handler_unittest.mm b/ios/chrome/browser/ssl/ios_ssl_error_handler_unittest.mm
index b5e4f1b4..5f2643f 100644
--- a/ios/chrome/browser/ssl/ios_ssl_error_handler_unittest.mm
+++ b/ios/chrome/browser/ssl/ios_ssl_error_handler_unittest.mm
@@ -15,6 +15,7 @@
 #include "net/ssl/ssl_info.h"
 #include "net/test/cert_test_util.h"
 #include "net/test/test_data_directory.h"
+#include "services/network/test/test_url_loader_factory.h"
 #import "third_party/ocmock/OCMock/OCMock.h"
 
 #if !defined(__has_feature) || !__has_feature(objc_arc)
@@ -39,8 +40,11 @@
     web::WebTestWithWebState::SetUp();
     id captive_portal_detector_tab_helper_delegate = [OCMockObject
         mockForProtocol:@protocol(CaptivePortalDetectorTabHelperDelegate)];
+    // Use a testing URLLoaderFactory so that these tests don't attempt to make
+    // network requests.
     CaptivePortalDetectorTabHelper::CreateForWebState(
-        web_state(), captive_portal_detector_tab_helper_delegate);
+        web_state(), captive_portal_detector_tab_helper_delegate,
+        &test_loader_factory_);
     ASSERT_TRUE(cert_);
     ASSERT_FALSE(web_state()->IsShowingWebInterstitial());
 
@@ -54,6 +58,7 @@
   scoped_refptr<net::X509Certificate> cert() { return cert_; }
 
  private:
+  network::TestURLLoaderFactory test_loader_factory_;
   TestChromeBrowserState::Builder builder_;
   std::unique_ptr<TestChromeBrowserState> browser_state_;
   scoped_refptr<net::X509Certificate> cert_;
diff --git a/ios/chrome/browser/ui/browser_view_controller.mm b/ios/chrome/browser/ui/browser_view_controller.mm
index 75a621e..e35f1b1c 100644
--- a/ios/chrome/browser/ui/browser_view_controller.mm
+++ b/ios/chrome/browser/ui/browser_view_controller.mm
@@ -169,6 +169,7 @@
 #import "ios/chrome/browser/ui/new_foreground_tab_fullscreen_disabler.h"
 #import "ios/chrome/browser/ui/ntp/new_tab_page_controller.h"
 #import "ios/chrome/browser/ui/ntp/recent_tabs/recent_tabs_handset_coordinator.h"
+#import "ios/chrome/browser/ui/ntp/recent_tabs/recent_tabs_table_coordinator.h"
 #import "ios/chrome/browser/ui/overscroll_actions/overscroll_actions_controller.h"
 #import "ios/chrome/browser/ui/page_info/page_info_legacy_coordinator.h"
 #import "ios/chrome/browser/ui/page_info/requirements/page_info_presentation.h"
@@ -668,8 +669,7 @@
 @property(nonatomic, strong, nullable)
     ProceduralBlock foregroundTabWasAddedCompletionBlock;
 // Coordinator for Recent Tabs.
-@property(nonatomic, strong)
-    RecentTabsHandsetCoordinator* recentTabsCoordinator;
+@property(nonatomic, strong) ChromeCoordinator* recentTabsCoordinator;
 // Coordinator for tablet tab strip.
 @property(nonatomic, strong) TabStripLegacyCoordinator* tabStripCoordinator;
 // A weak reference to the view of the tab strip on tablet.
@@ -931,6 +931,13 @@
 // Adds the given url to the reading list.
 - (void)addToReadingListURL:(const GURL&)URL title:(NSString*)title;
 
+// Recent Tabs
+// ------------
+// Creates the right RecentTabs Coordinator, once we stop supporting the legacy
+// implementation we can delete this method and start the coordinator on
+// |showRecentTabs|.
+- (void)createRecentTabsCoordinator;
+
 @end
 
 @implementation BrowserViewController
@@ -3129,6 +3136,26 @@
                          IDS_IOS_READING_LIST_SNACKBAR_MESSAGE)];
 }
 
+#pragma mark - Private Methods: Recent Tabs
+
+- (void)createRecentTabsCoordinator {
+  if (experimental_flags::IsRecentTabsUIRebootEnabled()) {
+    // New RecentTabs UIReboot coordinator.
+    RecentTabsTableCoordinator* recentTabsCoordinator =
+        [[RecentTabsTableCoordinator alloc] initWithBaseViewController:self];
+    recentTabsCoordinator.dispatcher = self.dispatcher;
+    self.recentTabsCoordinator = recentTabsCoordinator;
+  } else {
+    // Legacy RecentTabs coordinator.
+    RecentTabsHandsetCoordinator* recentTabsCoordinator =
+        [[RecentTabsHandsetCoordinator alloc] initWithBaseViewController:self];
+    recentTabsCoordinator.loader = self;
+    recentTabsCoordinator.dispatcher = self.dispatcher;
+    recentTabsCoordinator.browserState = _browserState;
+    self.recentTabsCoordinator = recentTabsCoordinator;
+  }
+}
+
 #pragma mark - ** Protocol Implementations and Helpers **
 
 #pragma mark - SnapshotGeneratorDelegate methods
@@ -3866,29 +3893,39 @@
 
 - (void)finishFullscreenScrollWithAnimator:
     (FullscreenScrollEndAnimator*)animator {
-  BOOL showingToolbar = animator.finalProgress > animator.startProgress;
+  // If the headers are being hidden, it's possible that this will reveal a
+  // portion of the webview beyond the top of the page's rendered content.  In
+  // order to prevent that, update the top padding and content before the
+  // animation begins.
   CGFloat finalProgress = animator.finalProgress;
-  // WKWebView does not re-render its content until its model layer's bounds
-  // have been updated at the end of the animation.  If the animator is going
-  // to hide the toolbar, update the content view's top padding early so that
-  // content is correctly rendered behind the toolbar that's being animated
-  // away.
-  if (!showingToolbar)
-    [self updateContentViewTopPaddingForFullscreenProgress:finalProgress];
-  [animator addAnimations:^{
-    [self updateHeadersForFullscreenProgress:finalProgress];
-    [self updateFootersForFullscreenProgress:finalProgress];
-  }];
-  // If the toolbar is being animated to become visible, update the content view
-  // top padding in the completion block so that fixed-position elements can be
-  // properly laid out in the new viewport.
-  if (showingToolbar) {
-    __weak FullscreenScrollEndAnimator* weakAnimator = animator;
-    [animator addCompletion:^(UIViewAnimatingPosition finalPosition) {
-      [self updateContentViewTopPaddingForFullscreenProgress:
-                [weakAnimator progressForAnimatingPosition:finalPosition]];
-    }];
+  BOOL hidingHeaders = animator.finalProgress < animator.startProgress;
+  if (hidingHeaders) {
+    id<CRWWebViewProxy> webProxy = self.currentWebState->GetWebViewProxy();
+    CRWWebViewScrollViewProxy* scrollProxy = webProxy.scrollViewProxy;
+    CGPoint contentOffset = scrollProxy.contentOffset;
+    if (contentOffset.y - scrollProxy.contentInset.top <
+        webProxy.topContentPadding) {
+      [self updateContentViewTopPaddingForFullscreenProgress:finalProgress];
+      contentOffset.y = -scrollProxy.contentInset.top;
+      scrollProxy.contentOffset = contentOffset;
+    }
   }
+
+  // Add animations to update the headers and footers.
+  __weak BrowserViewController* weakSelf = self;
+  [animator addAnimations:^{
+    [weakSelf updateHeadersForFullscreenProgress:finalProgress];
+    [weakSelf updateFootersForFullscreenProgress:finalProgress];
+  }];
+
+  // Animating layout changes of the rendered content in the WKWebView is not
+  // supported, so update the content padding in the completion block of the
+  // animator to trigger a rerender in the page's new viewport.
+  __weak FullscreenScrollEndAnimator* weakAnimator = animator;
+  [animator addCompletion:^(UIViewAnimatingPosition finalPosition) {
+    [weakSelf updateContentViewTopPaddingForFullscreenProgress:
+                  [weakAnimator progressForAnimatingPosition:finalPosition]];
+  }];
 }
 
 - (void)scrollFullscreenToTopWithAnimator:
@@ -4677,13 +4714,8 @@
 }
 
 - (void)showRecentTabs {
-  if (!self.recentTabsCoordinator) {
-    self.recentTabsCoordinator =
-        [[RecentTabsHandsetCoordinator alloc] initWithBaseViewController:self];
-    self.recentTabsCoordinator.loader = self;
-    self.recentTabsCoordinator.dispatcher = self.dispatcher;
-    self.recentTabsCoordinator.browserState = _browserState;
-  }
+  if (!self.recentTabsCoordinator)
+    [self createRecentTabsCoordinator];
   [self.recentTabsCoordinator start];
 }
 
diff --git a/ios/chrome/browser/ui/ntp/recent_tabs/BUILD.gn b/ios/chrome/browser/ui/ntp/recent_tabs/BUILD.gn
index 72c48bdf..2ac1184 100644
--- a/ios/chrome/browser/ui/ntp/recent_tabs/BUILD.gn
+++ b/ios/chrome/browser/ui/ntp/recent_tabs/BUILD.gn
@@ -18,6 +18,8 @@
     "recent_tabs_handset_coordinator.mm",
     "recent_tabs_handset_view_controller.h",
     "recent_tabs_handset_view_controller.mm",
+    "recent_tabs_table_coordinator.h",
+    "recent_tabs_table_coordinator.mm",
     "sessions_sync_user_state.h",
     "synced_sessions.h",
     "synced_sessions.mm",
@@ -45,6 +47,7 @@
     "//ios/chrome/browser/ui/ntp/recent_tabs/views",
     "//ios/chrome/browser/ui/settings/sync_utils",
     "//ios/chrome/browser/ui/signin_interaction/public",
+    "//ios/chrome/browser/ui/table_view",
     "//ios/chrome/browser/ui/util",
     "//ios/web",
     "//ui/base",
diff --git a/ios/chrome/browser/ui/ntp/recent_tabs/recent_tabs_table_coordinator.h b/ios/chrome/browser/ui/ntp/recent_tabs/recent_tabs_table_coordinator.h
new file mode 100644
index 0000000..53ba7d53
--- /dev/null
+++ b/ios/chrome/browser/ui/ntp/recent_tabs/recent_tabs_table_coordinator.h
@@ -0,0 +1,21 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef IOS_CHROME_BROWSER_UI_NTP_RECENT_TABS_RECENT_TABS_TABLE_COORDINATOR_H_
+#define IOS_CHROME_BROWSER_UI_NTP_RECENT_TABS_RECENT_TABS_TABLE_COORDINATOR_H_
+
+#import <UIKit/UIKit.h>
+
+#import "ios/chrome/browser/ui/coordinators/chrome_coordinator.h"
+
+@protocol BrowserCommands;
+
+@interface RecentTabsTableCoordinator : ChromeCoordinator
+
+// The dispatcher for this Coordinator.
+@property(nonatomic, weak) id<BrowserCommands> dispatcher;
+
+@end
+
+#endif  // IOS_CHROME_BROWSER_UI_NTP_RECENT_TABS_RECENT_TABS_TABLE_COORDINATOR_H_
diff --git a/ios/chrome/browser/ui/ntp/recent_tabs/recent_tabs_table_coordinator.mm b/ios/chrome/browser/ui/ntp/recent_tabs/recent_tabs_table_coordinator.mm
new file mode 100644
index 0000000..0e64f4c
--- /dev/null
+++ b/ios/chrome/browser/ui/ntp/recent_tabs/recent_tabs_table_coordinator.mm
@@ -0,0 +1,59 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#import "ios/chrome/browser/ui/ntp/recent_tabs/recent_tabs_table_coordinator.h"
+
+#import "ios/chrome/browser/ui/table_view/chrome_table_view_controller.h"
+#import "ios/chrome/browser/ui/table_view/table_container_view_controller.h"
+#import "ios/chrome/browser/ui/util/form_sheet_navigation_controller.h"
+#include "ios/chrome/grit/ios_strings.h"
+#include "ui/base/l10n/l10n_util.h"
+
+#if !defined(__has_feature) || !__has_feature(objc_arc)
+#error "This file requires ARC support."
+#endif
+
+@interface RecentTabsTableCoordinator ()
+// ViewController being managed by this Coordinator.
+@property(nonatomic, strong)
+    TableContainerViewController* recentTabsContainerViewController;
+@end
+
+@implementation RecentTabsTableCoordinator
+@synthesize dispatcher = _dispatcher;
+@synthesize recentTabsContainerViewController =
+    _recentTabsContainerViewController;
+
+- (void)start {
+  // Initialize and configure RecentTabsViewController.
+  self.recentTabsContainerViewController = [[TableContainerViewController alloc]
+      initWithTable:[[ChromeTableViewController alloc]
+                        initWithStyle:UITableViewStylePlain]];
+  self.recentTabsContainerViewController.title =
+      l10n_util::GetNSString(IDS_IOS_CONTENT_SUGGESTIONS_RECENT_TABS);
+  // TODO(crbug.com/805135): Move this configuration code to
+  // RecentTabsContainerVC once its created, we will use a dispatcher then.
+  [self.recentTabsContainerViewController.dismissButton setTarget:self];
+  [self.recentTabsContainerViewController.dismissButton
+      setAction:@selector(stop)];
+  self.recentTabsContainerViewController.navigationItem.rightBarButtonItem =
+      self.recentTabsContainerViewController.dismissButton;
+
+  // Present RecentTabsViewController.
+  FormSheetNavigationController* navController =
+      [[FormSheetNavigationController alloc]
+          initWithRootViewController:self.recentTabsContainerViewController];
+  [navController setModalPresentationStyle:UIModalPresentationFormSheet];
+  [self.baseViewController presentViewController:navController
+                                        animated:YES
+                                      completion:nil];
+}
+
+- (void)stop {
+  [self.recentTabsContainerViewController dismissViewControllerAnimated:YES
+                                                             completion:nil];
+  self.recentTabsContainerViewController = nil;
+}
+
+@end
diff --git a/ios/chrome/browser/ui/settings/BUILD.gn b/ios/chrome/browser/ui/settings/BUILD.gn
index ea5ce20..2a5bf898 100644
--- a/ios/chrome/browser/ui/settings/BUILD.gn
+++ b/ios/chrome/browser/ui/settings/BUILD.gn
@@ -155,6 +155,7 @@
     "//ios/chrome/browser/ui/signin_interaction",
     "//ios/chrome/browser/ui/signin_interaction/public",
     "//ios/chrome/browser/ui/table_view",
+    "//ios/chrome/browser/ui/table_view/cells",
     "//ios/chrome/browser/voice",
     "//ios/chrome/browser/web:web",
     "//ios/chrome/common",
diff --git a/ios/chrome/browser/ui/settings/table_cell_catalog_view_controller.mm b/ios/chrome/browser/ui/settings/table_cell_catalog_view_controller.mm
index 05e8f3ed..3df18af 100644
--- a/ios/chrome/browser/ui/settings/table_cell_catalog_view_controller.mm
+++ b/ios/chrome/browser/ui/settings/table_cell_catalog_view_controller.mm
@@ -4,12 +4,26 @@
 
 #import "ios/chrome/browser/ui/settings/table_cell_catalog_view_controller.h"
 
+#import "ios/chrome/browser/ui/table_view/cells/table_view_url_item.h"
 #import "ios/chrome/browser/ui/table_view/table_view_model.h"
 
 #if !defined(__has_feature) || !__has_feature(objc_arc)
 #error "This file requires ARC support."
 #endif
 
+namespace {
+
+typedef NS_ENUM(NSInteger, SectionIdentifier) {
+  SectionIdentifierURL = kSectionIdentifierEnumZero,
+};
+
+typedef NS_ENUM(NSInteger, ItemType) {
+  ItemTypeURLNoMetadata = kItemTypeEnumZero,
+  ItemTypeURLWithTimestamp,
+  ItemTypeURLWithSize,
+};
+}
+
 @implementation TableCellCatalogViewController
 
 - (instancetype)init {
@@ -21,12 +35,38 @@
 - (void)viewDidLoad {
   [super viewDidLoad];
   self.title = @"Table Cell Catalog";
+  self.tableView.rowHeight = UITableViewAutomaticDimension;
+  self.tableView.estimatedRowHeight = 56.0;
 
   [self loadModel];
 }
 
 - (void)loadModel {
   [super loadModel];
+
+  TableViewModel* model = self.tableViewModel;
+  [model addSectionWithIdentifier:SectionIdentifierURL];
+
+  TableViewURLItem* item =
+      [[TableViewURLItem alloc] initWithType:ItemTypeURLNoMetadata];
+  item.favicon = [UIImage imageNamed:@"default_favicon"];
+  item.title = @"Google Design";
+  item.URL = @"design.google.com";
+  [model addItem:item toSectionWithIdentifier:SectionIdentifierURL];
+
+  item = [[TableViewURLItem alloc] initWithType:ItemTypeURLWithTimestamp];
+  item.favicon = [UIImage imageNamed:@"default_favicon"];
+  item.title = @"Google";
+  item.URL = @"google.com";
+  item.metadata = @"3:42 PM";
+  [model addItem:item toSectionWithIdentifier:SectionIdentifierURL];
+
+  item = [[TableViewURLItem alloc] initWithType:ItemTypeURLWithSize];
+  item.favicon = [UIImage imageNamed:@"default_favicon"];
+  item.title = @"World Series 2017: Houston Astros Defeat Someone Else";
+  item.URL = @"m.bbc.com";
+  item.metadata = @"176 KB";
+  [model addItem:item toSectionWithIdentifier:SectionIdentifierURL];
 }
 
 @end
diff --git a/ios/chrome/browser/ui/tab_grid/BUILD.gn b/ios/chrome/browser/ui/tab_grid/BUILD.gn
index 98731002..186791a7 100644
--- a/ios/chrome/browser/ui/tab_grid/BUILD.gn
+++ b/ios/chrome/browser/ui/tab_grid/BUILD.gn
@@ -33,6 +33,7 @@
     "grid_consumer.h",
     "grid_item.h",
     "grid_item.mm",
+    "grid_theme.h",
     "grid_view_controller.h",
     "grid_view_controller.mm",
     "tab_grid_bottom_toolbar.h",
diff --git a/ios/chrome/browser/ui/tab_grid/grid_cell.h b/ios/chrome/browser/ui/tab_grid/grid_cell.h
index e82f9187..fba0ab45 100644
--- a/ios/chrome/browser/ui/tab_grid/grid_cell.h
+++ b/ios/chrome/browser/ui/tab_grid/grid_cell.h
@@ -7,23 +7,26 @@
 
 #import <UIKit/UIKit.h>
 
-@class GridItem;
+#import "ios/chrome/browser/ui/tab_grid/grid_theme.h"
 
-// Theme describing the look of the cell.
-typedef NS_ENUM(NSUInteger, GridCellTheme) {
-  GridCellThemeInvalid = 0,
-  GridCellThemeLight,
-  GridCellThemeDark,
-};
+@class GridCell;
+
+// Informs the receiver of actions on the cell.
+@protocol GridCellDelegate
+- (void)closeButtonTappedForCell:(GridCell*)cell;
+@end
 
 // A square-ish cell in a grid. Contains an icon, title, snapshot, and close
 // button.
 @interface GridCell : UICollectionViewCell
-
-// Configures cell with contents of |item|. |theme| specifies the look of the
-// cell.
-- (void)configureWithItem:(GridItem*)item theme:(GridCellTheme)theme;
-
+// Delegate to inform the grid of actions on the cell.
+@property(nonatomic, weak) id<GridCellDelegate> delegate;
+// The look of the cell.
+@property(nonatomic, assign) GridTheme theme;
+// Settable UI elements of the cell.
+@property(nonatomic, weak) UIImage* icon;
+@property(nonatomic, weak) UIImage* snapshot;
+@property(nonatomic, copy) NSString* title;
 @end
 
 #endif  // IOS_CHROME_BROWSER_UI_TAB_GRID_GRID_CELL_H_
diff --git a/ios/chrome/browser/ui/tab_grid/grid_cell.mm b/ios/chrome/browser/ui/tab_grid/grid_cell.mm
index e3c3d58d..0031372 100644
--- a/ios/chrome/browser/ui/tab_grid/grid_cell.mm
+++ b/ios/chrome/browser/ui/tab_grid/grid_cell.mm
@@ -5,153 +5,242 @@
 #import "ios/chrome/browser/ui/tab_grid/grid_cell.h"
 
 #import "base/logging.h"
-#import "ios/chrome/browser/ui/tab_grid/grid_item.h"
-#import "ios/chrome/browser/ui/util/constraints_ui_util.h"
+#import "ios/chrome/browser/ui/tab_grid/top_aligned_image_view.h"
 
 #if !defined(__has_feature) || !__has_feature(objc_arc)
 #error "This file requires ARC support."
 #endif
 
+namespace {
+// Height of the top bar containing the icon, title, and close button.
+const CGFloat kTopBarHeight = 28.0f;
+// Width and height of the icon.
+const CGFloat kIconDiameter = 20.0f;
+// Width and height of the close button.
+const CGFloat kCloseButtonDiameter = 16.0f;
+// Width of selection border.
+const CGFloat kBorderWidth = 6.0f;
+}  // namespace
+
 @interface GridCell ()
-// A boolean to ensure setup is completed once.
-@property(nonatomic, assign) BOOL isSetup;
-// Current theme of the cell.
-@property(nonatomic, assign) GridCellTheme currentTheme;
 // Visual components of the cell.
-@property(nonatomic, strong) UIView* topBar;
-@property(nonatomic, strong) UIView* line;
-@property(nonatomic, strong) UIImageView* icon;
-@property(nonatomic, strong) UIImageView* snapshot;
-@property(nonatomic, strong) UILabel* titleLabel;
-@property(nonatomic, strong) UIButton* closeButton;
+@property(nonatomic, weak) UIView* topBar;
+@property(nonatomic, weak) UIView* line;
+@property(nonatomic, weak) UIImageView* iconView;
+@property(nonatomic, weak) TopAlignedImageView* snapshotView;
+@property(nonatomic, weak) UILabel* titleLabel;
+@property(nonatomic, weak) UIButton* closeButton;
+@property(nonatomic, weak) UIView* border;
 @end
 
 @implementation GridCell
-@synthesize isSetup = _isSetup;
-@synthesize currentTheme = _currentTheme;
-@synthesize topBar = _topBar;
-@synthesize line = _line;
+// Public properties.
+@synthesize delegate = _delegate;
+@synthesize theme = _theme;
 @synthesize icon = _icon;
 @synthesize snapshot = _snapshot;
+@synthesize title = _title;
+// Private properties.
+@synthesize topBar = _topBar;
+@synthesize line = _line;
+@synthesize iconView = _iconView;
+@synthesize snapshotView = _snapshotView;
 @synthesize titleLabel = _titleLabel;
 @synthesize closeButton = _closeButton;
+@synthesize border = _border;
+
+// |-dequeueReusableCellWithReuseIdentifier:forIndexPath:| calls this method to
+// initialize a cell.
+- (instancetype)initWithFrame:(CGRect)frame {
+  self = [super initWithFrame:frame];
+  if (self) {
+    [self setupSelectedBackgroundView];
+    UIView* contentView = self.contentView;
+    contentView.layer.cornerRadius = 11.0f;
+    contentView.layer.masksToBounds = YES;
+    UIView* topBar = [self setupTopBar];
+    UIView* line = [[UIView alloc] init];
+    line.translatesAutoresizingMaskIntoConstraints = NO;
+    TopAlignedImageView* snapshotView = [[TopAlignedImageView alloc] init];
+    snapshotView.translatesAutoresizingMaskIntoConstraints = NO;
+    [contentView addSubview:topBar];
+    [contentView addSubview:line];
+    [contentView addSubview:snapshotView];
+    _topBar = topBar;
+    _line = line;
+    _snapshotView = snapshotView;
+    NSArray* constraints = @[
+      [topBar.topAnchor constraintEqualToAnchor:contentView.topAnchor],
+      [topBar.leadingAnchor constraintEqualToAnchor:contentView.leadingAnchor],
+      [topBar.trailingAnchor
+          constraintEqualToAnchor:contentView.trailingAnchor],
+      [topBar.heightAnchor constraintEqualToConstant:kTopBarHeight],
+      [line.topAnchor constraintEqualToAnchor:topBar.bottomAnchor],
+      [line.leadingAnchor constraintEqualToAnchor:contentView.leadingAnchor],
+      [line.trailingAnchor constraintEqualToAnchor:contentView.trailingAnchor],
+      [line.heightAnchor constraintEqualToConstant:0.5f],
+      [snapshotView.topAnchor constraintEqualToAnchor:line.bottomAnchor],
+      [snapshotView.leadingAnchor
+          constraintEqualToAnchor:contentView.leadingAnchor],
+      [snapshotView.trailingAnchor
+          constraintEqualToAnchor:contentView.trailingAnchor],
+      [snapshotView.bottomAnchor
+          constraintEqualToAnchor:contentView.bottomAnchor],
+    ];
+    [NSLayoutConstraint activateConstraints:constraints];
+  }
+  return self;
+}
+
+#pragma mark - UICollectionViewCell
+
+- (void)setHighlighted:(BOOL)highlighted {
+  // NO-OP to disable highlighting and only allow selection.
+}
 
 #pragma mark - Public
 
-- (void)configureWithItem:(GridItem*)item theme:(GridCellTheme)theme {
-  [self setupContentViewIfNeeded];
-  [self updateThemeIfNeeded:theme];
-  self.titleLabel.text = item.title;
+// Updates the theme to either dark or light. Updating is only done if the
+// current theme is not the desired theme.
+- (void)setTheme:(GridTheme)theme {
+  if (_theme == theme)
+    return;
+  switch (theme) {
+    case GridThemeLight:
+      self.topBar.backgroundColor = [UIColor whiteColor];
+      self.iconView.backgroundColor = [UIColor colorWithWhite:0.9f alpha:1.0f];
+      self.titleLabel.textColor = [UIColor blackColor];
+      self.line.backgroundColor = [UIColor colorWithWhite:0.6f alpha:1.0f];
+      self.snapshotView.backgroundColor = [UIColor whiteColor];
+      self.closeButton.backgroundColor =
+          [UIColor colorWithWhite:0.6f alpha:1.0f];
+      self.closeButton.tintColor = [UIColor whiteColor];
+      self.tintColor =
+          [UIColor colorWithRed:0.0 green:122.0 / 255.0 blue:1.0 alpha:1.0];
+      self.border.layer.borderColor = self.tintColor.CGColor;
+      break;
+    case GridThemeDark:
+      self.topBar.backgroundColor = [UIColor colorWithWhite:0.2f alpha:1.0f];
+      self.iconView.backgroundColor = [UIColor colorWithWhite:0.9f alpha:1.0f];
+      self.titleLabel.textColor = [UIColor whiteColor];
+      self.line.backgroundColor = [UIColor colorWithWhite:0.2f alpha:1.0f];
+      self.snapshotView.backgroundColor =
+          [UIColor colorWithWhite:0.4f alpha:1.0f];
+      self.closeButton.backgroundColor =
+          [UIColor colorWithWhite:0.4f alpha:1.0f];
+      self.closeButton.tintColor = [UIColor colorWithWhite:0.2f alpha:1.0f];
+      self.tintColor = [UIColor colorWithWhite:0.9f alpha:1.0f];
+      self.border.layer.borderColor = self.tintColor.CGColor;
+      break;
+    default:
+      NOTREACHED() << "Invalid GridTheme.";
+      break;
+  }
+  _theme = theme;
+}
+
+- (void)setIcon:(UIImage*)icon {
+  self.iconView.image = icon;
+  _icon = icon;
+}
+
+- (void)setSnapshot:(UIImage*)snapshot {
+  self.snapshotView.image = snapshot;
+  _snapshot = snapshot;
+}
+
+- (void)setTitle:(NSString*)title {
+  self.titleLabel.text = title;
+  _title = title;
 }
 
 #pragma mark - Private
 
-// Adds all the subviews onto the contentView. Activates all constraints. The
-// property |isSetup| ensures that this method is only called once.
-- (void)setupContentViewIfNeeded {
-  if (self.isSetup)
-    return;
-
-  self.contentView.layer.cornerRadius = 11.0f;
-  self.contentView.layer.masksToBounds = YES;
-
-  self.topBar = [self setupTopBar];
-  self.line = [[UIView alloc] init];
-  self.line.translatesAutoresizingMaskIntoConstraints = NO;
-  self.snapshot = [[UIImageView alloc] initWithFrame:CGRectZero];
-  self.snapshot.translatesAutoresizingMaskIntoConstraints = NO;
-
-  [self.contentView addSubview:self.topBar];
-  [self.contentView addSubview:self.line];
-  [self.contentView addSubview:self.snapshot];
-  NSDictionary* views = @{
-    @"bar" : self.topBar,
-    @"line" : self.line,
-    @"snapshot" : self.snapshot,
-  };
-  NSArray* constraints = @[
-    @"H:|-0-[bar]-0-|",
-    @"H:|-0-[line]-0-|",
-    @"H:|-0-[snapshot]-0-|",
-    @"V:|-0-[bar(==28)]-0-[line(==0.5)]-0-[snapshot]-0-|",
-  ];
-  ApplyVisualConstraints(constraints, views, self.contentView);
-  self.isSetup = YES;
-}
-
 // Sets up the top bar with icon, title, and close button.
 - (UIView*)setupTopBar {
-  UIView* topBar = [[UIView alloc] initWithFrame:CGRectZero];
+  UIView* topBar = [[UIView alloc] init];
   topBar.translatesAutoresizingMaskIntoConstraints = NO;
 
-  self.icon = [[UIImageView alloc] initWithFrame:CGRectZero];
-  self.icon.translatesAutoresizingMaskIntoConstraints = NO;
-  self.icon.layer.cornerRadius = 2.0f;
-  self.icon.layer.masksToBounds = YES;
+  UIImageView* iconView = [[UIImageView alloc] init];
+  iconView.translatesAutoresizingMaskIntoConstraints = NO;
+  iconView.contentMode = UIViewContentModeScaleAspectFill;
+  iconView.layer.cornerRadius = 4.0f;
+  iconView.layer.masksToBounds = YES;
 
-  self.titleLabel = [[UILabel alloc] initWithFrame:CGRectZero];
-  self.titleLabel.translatesAutoresizingMaskIntoConstraints = NO;
-  self.titleLabel.font = [UIFont boldSystemFontOfSize:12.0f];
+  UILabel* titleLabel = [[UILabel alloc] init];
+  titleLabel.translatesAutoresizingMaskIntoConstraints = NO;
+  titleLabel.font = [UIFont boldSystemFontOfSize:12.0f];
 
-  self.closeButton = [UIButton buttonWithType:UIButtonTypeCustom];
-  self.closeButton.translatesAutoresizingMaskIntoConstraints = NO;
-  self.closeButton.layer.cornerRadius = 8.0f;
-  self.closeButton.layer.masksToBounds = YES;
+  UIButton* closeButton = [UIButton buttonWithType:UIButtonTypeCustom];
+  closeButton.translatesAutoresizingMaskIntoConstraints = NO;
+  closeButton.layer.cornerRadius = 8.0f;
+  closeButton.layer.masksToBounds = YES;
   UIImage* closeImage = [[UIImage imageNamed:@"card_close_button"]
       imageWithRenderingMode:UIImageRenderingModeAlwaysTemplate];
-  [self.closeButton setImage:closeImage forState:UIControlStateNormal];
+  [closeButton setImage:closeImage forState:UIControlStateNormal];
+  [closeButton addTarget:self
+                  action:@selector(closeButtonTapped:)
+        forControlEvents:UIControlEventTouchUpInside];
 
-  [topBar addSubview:self.icon];
-  [topBar addSubview:self.titleLabel];
-  [topBar addSubview:self.closeButton];
-  NSDictionary* views = @{
-    @"icon" : self.icon,
-    @"title" : self.titleLabel,
-    @"close" : self.closeButton,
-  };
+  [topBar addSubview:iconView];
+  [topBar addSubview:titleLabel];
+  [topBar addSubview:closeButton];
+  _iconView = iconView;
+  _titleLabel = titleLabel;
+  _closeButton = closeButton;
+
   NSArray* constraints = @[
-    @"H:|-4-[icon(==20)]-4-[title]-0-[close(==16)]-6-|",
-    @"V:[icon(==20)]",
-    @"V:[close(==16)]",
+    [iconView.leadingAnchor constraintEqualToAnchor:topBar.leadingAnchor
+                                           constant:4.0f],
+    [iconView.centerYAnchor constraintEqualToAnchor:topBar.centerYAnchor],
+    [iconView.widthAnchor constraintEqualToConstant:kIconDiameter],
+    [iconView.heightAnchor constraintEqualToConstant:kIconDiameter],
+    [titleLabel.leadingAnchor constraintEqualToAnchor:iconView.trailingAnchor
+                                             constant:4.0f],
+    [titleLabel.centerYAnchor constraintEqualToAnchor:topBar.centerYAnchor],
+    [closeButton.leadingAnchor
+        constraintEqualToAnchor:titleLabel.trailingAnchor],
+    [closeButton.centerYAnchor constraintEqualToAnchor:topBar.centerYAnchor],
+    [closeButton.widthAnchor constraintEqualToConstant:kCloseButtonDiameter],
+    [closeButton.heightAnchor constraintEqualToConstant:kCloseButtonDiameter],
+    [closeButton.trailingAnchor constraintEqualToAnchor:topBar.trailingAnchor
+                                               constant:-6.0f],
   ];
-  ApplyVisualConstraints(constraints, views, topBar);
-  AddSameCenterYConstraint(topBar, self.icon);
-  AddSameCenterYConstraint(topBar, self.titleLabel);
-  AddSameCenterYConstraint(topBar, self.closeButton);
+  [NSLayoutConstraint activateConstraints:constraints];
   return topBar;
 }
 
-// Updates the theme to either dark or light. Updating is only done if the
-// current theme is not the desired theme.
-- (void)updateThemeIfNeeded:(GridCellTheme)theme {
-  if (self.currentTheme == theme)
-    return;
-  switch (theme) {
-    case GridCellThemeLight:
-      self.topBar.backgroundColor = [UIColor whiteColor];
-      self.icon.backgroundColor = [UIColor colorWithWhite:0.9f alpha:1.0f];
-      self.titleLabel.textColor = [UIColor blackColor];
-      self.line.backgroundColor = [UIColor colorWithWhite:0.6f alpha:1.0f];
-      self.snapshot.backgroundColor = [UIColor whiteColor];
-      self.closeButton.backgroundColor =
-          [UIColor colorWithWhite:0.6f alpha:1.0f];
-      self.closeButton.tintColor = [UIColor whiteColor];
-      break;
-    case GridCellThemeDark:
-      self.topBar.backgroundColor = [UIColor colorWithWhite:0.2f alpha:1.0f];
-      self.icon.backgroundColor = [UIColor colorWithWhite:0.9f alpha:1.0f];
-      self.titleLabel.textColor = [UIColor whiteColor];
-      self.line.backgroundColor = [UIColor colorWithWhite:0.2f alpha:1.0f];
-      self.snapshot.backgroundColor = [UIColor colorWithWhite:0.4f alpha:1.0f];
-      self.closeButton.backgroundColor =
-          [UIColor colorWithWhite:0.4f alpha:1.0f];
-      self.closeButton.tintColor = [UIColor colorWithWhite:0.2f alpha:1.0f];
-      break;
-    default:
-      NOTREACHED() << "Invalid GridCellTheme.";
-      break;
-  }
-  self.currentTheme = theme;
+// Sets up the selection border. The tint color is set when the theme is
+// selected.
+- (void)setupSelectedBackgroundView {
+  self.selectedBackgroundView = [[UIView alloc] init];
+  self.selectedBackgroundView.backgroundColor = [UIColor blackColor];
+  UIView* border = [[UIView alloc] init];
+  border.translatesAutoresizingMaskIntoConstraints = NO;
+  border.backgroundColor = [UIColor blackColor];
+  border.layer.cornerRadius = 17.0f;
+  border.layer.borderWidth = 4.0f;
+  [self.selectedBackgroundView addSubview:border];
+  _border = border;
+  [NSLayoutConstraint activateConstraints:@[
+    [border.topAnchor
+        constraintEqualToAnchor:self.selectedBackgroundView.topAnchor
+                       constant:-kBorderWidth],
+    [border.leadingAnchor
+        constraintEqualToAnchor:self.selectedBackgroundView.leadingAnchor
+                       constant:-kBorderWidth],
+    [border.trailingAnchor
+        constraintEqualToAnchor:self.selectedBackgroundView.trailingAnchor
+                       constant:kBorderWidth],
+    [border.bottomAnchor
+        constraintEqualToAnchor:self.selectedBackgroundView.bottomAnchor
+                       constant:kBorderWidth]
+  ]];
+}
+
+// Selector registered to the close button.
+- (void)closeButtonTapped:(id)sender {
+  [self.delegate closeButtonTappedForCell:self];
 }
 
 @end
diff --git a/ios/chrome/browser/ui/tab_grid/grid_theme.h b/ios/chrome/browser/ui/tab_grid/grid_theme.h
new file mode 100644
index 0000000..1c0295d6
--- /dev/null
+++ b/ios/chrome/browser/ui/tab_grid/grid_theme.h
@@ -0,0 +1,15 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef IOS_CHROME_BROWSER_UI_TAB_GRID_GRID_THEME_H_
+#define IOS_CHROME_BROWSER_UI_TAB_GRID_GRID_THEME_H_
+
+// Theme describing the look of the grid.
+typedef NS_ENUM(NSUInteger, GridTheme) {
+  GridThemeInvalid = 0,
+  GridThemeLight,
+  GridThemeDark,
+};
+
+#endif  // IOS_CHROME_BROWSER_UI_TAB_GRID_GRID_THEME_H_
diff --git a/ios/chrome/browser/ui/tab_grid/grid_view_controller.mm b/ios/chrome/browser/ui/tab_grid/grid_view_controller.mm
index 9c577b2..3d5db39 100644
--- a/ios/chrome/browser/ui/tab_grid/grid_view_controller.mm
+++ b/ios/chrome/browser/ui/tab_grid/grid_view_controller.mm
@@ -73,7 +73,7 @@
   GridCell* cell = base::mac::ObjCCastStrict<GridCell>([collectionView
       dequeueReusableCellWithReuseIdentifier:kCellIdentifier
                                 forIndexPath:indexPath]);
-  [cell configureWithItem:self.items[indexPath.item] theme:GridCellThemeLight];
+  cell.title = self.items[indexPath.item].title;
   return cell;
 }
 
diff --git a/ios/chrome/browser/ui/table_view/BUILD.gn b/ios/chrome/browser/ui/table_view/BUILD.gn
index 56f5124..536c0f7 100644
--- a/ios/chrome/browser/ui/table_view/BUILD.gn
+++ b/ios/chrome/browser/ui/table_view/BUILD.gn
@@ -13,7 +13,9 @@
     "table_view_model.mm",
   ]
   deps = [
+    "//ios/chrome/app/strings",
     "//ios/chrome/browser/ui/list_model",
+    "//ui/base",
   ]
   public_deps = [
     "//ios/chrome/browser/ui/table_view/cells",
diff --git a/ios/chrome/browser/ui/table_view/cells/BUILD.gn b/ios/chrome/browser/ui/table_view/cells/BUILD.gn
index 65ef84f..f067b13 100644
--- a/ios/chrome/browser/ui/table_view/cells/BUILD.gn
+++ b/ios/chrome/browser/ui/table_view/cells/BUILD.gn
@@ -6,11 +6,14 @@
   sources = [
     "table_view_item.h",
     "table_view_item.mm",
+    "table_view_url_item.h",
+    "table_view_url_item.mm",
   ]
 
   deps = [
     "//base",
     "//ios/chrome/browser/ui/list_model",
+    "//ios/chrome/browser/ui/util:constraints_ui",
   ]
   configs += [ "//build/config/compiler:enable_arc" ]
 }
@@ -19,10 +22,12 @@
   testonly = true
   sources = [
     "table_view_item_unittest.mm",
+    "table_view_url_item_unittest.mm",
   ]
 
   deps = [
     ":cells",
+    "//base",
     "//testing/gtest",
   ]
 
diff --git a/ios/chrome/browser/ui/table_view/cells/table_view_url_item.h b/ios/chrome/browser/ui/table_view/cells/table_view_url_item.h
new file mode 100644
index 0000000..9150451
--- /dev/null
+++ b/ios/chrome/browser/ui/table_view/cells/table_view_url_item.h
@@ -0,0 +1,42 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef IOS_CHROME_BROWSER_UI_TABLE_VIEW_CELLS_TABLE_VIEW_URL_ITEM_H_
+#define IOS_CHROME_BROWSER_UI_TABLE_VIEW_CELLS_TABLE_VIEW_URL_ITEM_H_
+
+#import <UIKit/UIKit.h>
+
+#import "ios/chrome/browser/ui/table_view/cells/table_view_item.h"
+
+// TableViewURLItem contains the model data for a TableViewURLCell.
+@interface TableViewURLItem : TableViewItem
+
+@property(nonatomic, readwrite, strong) UIImage* favicon;
+@property(nonatomic, readwrite, copy) NSString* title;
+@property(nonatomic, readwrite, copy) NSString* URL;
+@property(nonatomic, readwrite, copy) NSString* metadata;
+
+@end
+
+// TableViewURLCell is used in Bookmarks, Reading List, and Recent Tabs.  It
+// contains a favicon, a title, a URL, and optionally some metadata such as a
+// timestamp or a file size.
+@interface TableViewURLCell : UITableViewCell
+
+// The imageview that is displayed on the leading edge of the cell.  This
+// contains a favicon composited on top of an off-white background.
+@property(nonatomic, readonly, strong) UIImageView* faviconView;
+
+// The cell title.
+@property(nonatomic, readonly, strong) UILabel* titleLabel;
+
+// The URL associated with this cell.
+@property(nonatomic, readonly, strong) UILabel* URLLabel;
+
+// Optional metadata that is displayed at the trailing edge of the cell.
+@property(nonatomic, readonly, strong) UILabel* metadataLabel;
+
+@end
+
+#endif  // IOS_CHROME_BROWSER_UI_TABLE_VIEW_CELLS_TABLE_VIEW_URL_ITEM_H_
diff --git a/ios/chrome/browser/ui/table_view/cells/table_view_url_item.mm b/ios/chrome/browser/ui/table_view/cells/table_view_url_item.mm
new file mode 100644
index 0000000..8c26b814
--- /dev/null
+++ b/ios/chrome/browser/ui/table_view/cells/table_view_url_item.mm
@@ -0,0 +1,141 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#import "ios/chrome/browser/ui/table_view/cells/table_view_url_item.h"
+
+#include "base/mac/foundation_util.h"
+#import "ios/chrome/browser/ui/util/constraints_ui_util.h"
+
+#if !defined(__has_feature) || !__has_feature(objc_arc)
+#error "This file requires ARC support."
+#endif
+
+namespace {
+
+// The horizontal spacing between text labels.
+const CGFloat kHorizontalSpacing = 8.0;
+
+// THe vertical spacing between text labels.
+const CGFloat kVerticalSpacing = 2.0;
+
+// The display size of the favicon view.
+const CGFloat kFaviconViewSize = 56.0;
+}
+
+@implementation TableViewURLItem
+
+@synthesize favicon = _favicon;
+@synthesize metadata = _metadata;
+@synthesize title = _title;
+@synthesize URL = _URL;
+
+- (instancetype)initWithType:(NSInteger)type {
+  self = [super initWithType:type];
+  if (self) {
+    self.cellClass = [TableViewURLCell class];
+  }
+  return self;
+}
+
+- (void)configureCell:(UITableViewCell*)tableCell {
+  [super configureCell:tableCell];
+
+  TableViewURLCell* cell =
+      base::mac::ObjCCastStrict<TableViewURLCell>(tableCell);
+  cell.faviconView.image = self.favicon;
+  cell.titleLabel.text = self.title;
+  cell.URLLabel.text = self.URL;
+  cell.metadataLabel.text = self.metadata;
+  cell.metadataLabel.hidden = ([self.metadata length] == 0);
+}
+
+@end
+
+@implementation TableViewURLCell
+@synthesize faviconView = _faviconView;
+@synthesize metadataLabel = _metadataLabel;
+@synthesize titleLabel = _titleLabel;
+@synthesize URLLabel = _URLLabel;
+
+- (instancetype)initWithStyle:(UITableViewCellStyle)style
+              reuseIdentifier:(NSString*)reuseIdentifier {
+  self = [super initWithStyle:style reuseIdentifier:reuseIdentifier];
+  if (self) {
+    _faviconView = [[UIImageView alloc] init];
+    _titleLabel = [[UILabel alloc] init];
+    _URLLabel = [[UILabel alloc] init];
+    _metadataLabel = [[UILabel alloc] init];
+
+    // The favicon image is smaller than its UIImageView's bounds, so center
+    // it.
+    _faviconView.contentMode = UIViewContentModeCenter;
+
+    // Set font sizes using dynamic type.
+    _titleLabel.font = [UIFont preferredFontForTextStyle:UIFontTextStyleBody];
+    _titleLabel.adjustsFontForContentSizeCategory = YES;
+    _URLLabel.font = [UIFont preferredFontForTextStyle:UIFontTextStyleCaption1];
+    _URLLabel.adjustsFontForContentSizeCategory = YES;
+    _metadataLabel.font =
+        [UIFont preferredFontForTextStyle:UIFontTextStyleCaption1];
+    _metadataLabel.adjustsFontForContentSizeCategory = YES;
+
+    // Use stack views to layout the subviews except for the favicon.
+    UIStackView* verticalStack = [[UIStackView alloc]
+        initWithArrangedSubviews:@[ _titleLabel, _URLLabel ]];
+    verticalStack.axis = UILayoutConstraintAxisVertical;
+
+    verticalStack.spacing = kVerticalSpacing;
+    [_metadataLabel setContentHuggingPriority:UILayoutPriorityDefaultHigh
+                                      forAxis:UILayoutConstraintAxisHorizontal];
+    [_metadataLabel
+        setContentCompressionResistancePriority:UILayoutPriorityDefaultHigh
+                                        forAxis:
+                                            UILayoutConstraintAxisHorizontal];
+
+    UIStackView* horizontalStack = [[UIStackView alloc]
+        initWithArrangedSubviews:@[ verticalStack, _metadataLabel ]];
+    horizontalStack.axis = UILayoutConstraintAxisHorizontal;
+    horizontalStack.spacing = kHorizontalSpacing;
+    horizontalStack.distribution = UIStackViewDistributionFill;
+    horizontalStack.alignment = UIStackViewAlignmentFirstBaseline;
+
+    UIView* contentView = self.contentView;
+    _faviconView.translatesAutoresizingMaskIntoConstraints = NO;
+    horizontalStack.translatesAutoresizingMaskIntoConstraints = NO;
+    [contentView addSubview:_faviconView];
+    [contentView addSubview:horizontalStack];
+
+    [NSLayoutConstraint activateConstraints:@[
+      // The favicon view is a fixed size, is pinned to the leading edge of the
+      // content view, and is centered vertically.
+      [_faviconView.heightAnchor constraintEqualToConstant:kFaviconViewSize],
+      [_faviconView.widthAnchor constraintEqualToConstant:kFaviconViewSize],
+      [_faviconView.leadingAnchor
+          constraintEqualToAnchor:self.contentView.leadingAnchor],
+      [_faviconView.centerYAnchor
+          constraintEqualToAnchor:self.contentView.centerYAnchor],
+
+      // The stack view fills the remaining space, has an intrinsic height, and
+      // is centered vertically.
+      [horizontalStack.leadingAnchor
+          constraintEqualToAnchor:_faviconView.trailingAnchor],
+      [horizontalStack.trailingAnchor
+          constraintEqualToAnchor:self.contentView.trailingAnchor
+                         constant:-kHorizontalSpacing],
+      [horizontalStack.centerYAnchor
+          constraintEqualToAnchor:self.contentView.centerYAnchor],
+
+      // The content view's height is set by the larger of the favicon view or
+      // the stack view.  This maintains a minimum size but allows the cell to
+      // grow if Dynamic Type increases the font size.
+      [self.contentView.heightAnchor
+          constraintGreaterThanOrEqualToAnchor:_faviconView.heightAnchor],
+      [self.contentView.heightAnchor
+          constraintGreaterThanOrEqualToAnchor:horizontalStack.heightAnchor],
+    ]];
+  }
+  return self;
+}
+
+@end
diff --git a/ios/chrome/browser/ui/table_view/cells/table_view_url_item_unittest.mm b/ios/chrome/browser/ui/table_view/cells/table_view_url_item_unittest.mm
new file mode 100644
index 0000000..bb35af6
--- /dev/null
+++ b/ios/chrome/browser/ui/table_view/cells/table_view_url_item_unittest.mm
@@ -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.
+
+#import "ios/chrome/browser/ui/table_view/cells/table_view_url_item.h"
+
+#include "base/mac/foundation_util.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "testing/gtest_mac.h"
+#include "testing/platform_test.h"
+
+#if !defined(__has_feature) || !__has_feature(objc_arc)
+#error "This file requires ARC support."
+#endif
+
+namespace {
+using TableViewURLItemTest = PlatformTest;
+}
+
+// Tests that the UILabels are set properly after a call to |configureCell:|.
+TEST_F(TableViewURLItemTest, TextLabels) {
+  NSString* titleText = @"Title text";
+  NSString* URLText = @"URL text";
+  NSString* metadataText = @"Metadata text";
+
+  TableViewURLItem* item = [[TableViewURLItem alloc] initWithType:0];
+  item.title = titleText;
+  item.URL = URLText;
+  item.metadata = metadataText;
+
+  id cell = [[[item cellClass] alloc] init];
+  ASSERT_TRUE([cell isMemberOfClass:[TableViewURLCell class]]);
+
+  TableViewURLCell* URLCell = base::mac::ObjCCastStrict<TableViewURLCell>(cell);
+  EXPECT_FALSE(URLCell.titleLabel.text);
+  EXPECT_FALSE(URLCell.URLLabel.text);
+  EXPECT_FALSE(URLCell.metadataLabel.text);
+
+  [item configureCell:URLCell];
+  EXPECT_NSEQ(titleText, URLCell.titleLabel.text);
+  EXPECT_NSEQ(URLText, URLCell.URLLabel.text);
+  EXPECT_NSEQ(metadataText, URLCell.metadataLabel.text);
+}
+
+TEST_F(TableViewURLItemTest, MetadataLabelIsHiddenWhenEmpty) {
+  NSString* metadataText = nil;
+
+  TableViewURLItem* item = [[TableViewURLItem alloc] initWithType:0];
+  item.metadata = metadataText;
+
+  id cell = [[[item cellClass] alloc] init];
+  ASSERT_TRUE([cell isMemberOfClass:[TableViewURLCell class]]);
+
+  TableViewURLCell* URLCell = base::mac::ObjCCastStrict<TableViewURLCell>(cell);
+  [item configureCell:URLCell];
+  EXPECT_TRUE(URLCell.metadataLabel.hidden);
+}
+
+TEST_F(TableViewURLItemTest, MetadataLabelIsVisibleWhenNonEmpty) {
+  NSString* metadataText = @"Metadata text";
+
+  TableViewURLItem* item = [[TableViewURLItem alloc] initWithType:0];
+  item.metadata = metadataText;
+
+  id cell = [[[item cellClass] alloc] init];
+  ASSERT_TRUE([cell isMemberOfClass:[TableViewURLCell class]]);
+
+  TableViewURLCell* URLCell = base::mac::ObjCCastStrict<TableViewURLCell>(cell);
+  [item configureCell:URLCell];
+  EXPECT_FALSE(URLCell.metadataLabel.hidden);
+}
diff --git a/ios/chrome/browser/ui/table_view/table_container_view_controller.h b/ios/chrome/browser/ui/table_view/table_container_view_controller.h
index 9716c7b6..0ee60e8d 100644
--- a/ios/chrome/browser/ui/table_view/table_container_view_controller.h
+++ b/ios/chrome/browser/ui/table_view/table_container_view_controller.h
@@ -23,6 +23,10 @@
 // The bottom toolbar owned by this ViewController.
 @property(nonatomic, strong) UIView* bottomToolbar;
 
+// UIBarButtonItem to be used on a Navigation Controller to dismiss this
+// ViewController.
+@property(nonatomic, strong, readonly) UIBarButtonItem* dismissButton;
+
 // The ChromeTableViewController owned by this ViewController.
 @property(nonatomic, strong) ChromeTableViewController* tableViewController;
 
diff --git a/ios/chrome/browser/ui/table_view/table_container_view_controller.mm b/ios/chrome/browser/ui/table_view/table_container_view_controller.mm
index 4c6c315..533ed2f 100644
--- a/ios/chrome/browser/ui/table_view/table_container_view_controller.mm
+++ b/ios/chrome/browser/ui/table_view/table_container_view_controller.mm
@@ -5,13 +5,21 @@
 #import "ios/chrome/browser/ui/table_view/table_container_view_controller.h"
 
 #import "ios/chrome/browser/ui/table_view/chrome_table_view_controller.h"
+#include "ios/chrome/grit/ios_strings.h"
+#include "ui/base/l10n/l10n_util.h"
 
 #if !defined(__has_feature) || !__has_feature(objc_arc)
 #error "This file requires ARC support."
 #endif
 
+@interface TableContainerViewController ()
+// Change dismissButton to readwrite.
+@property(nonatomic, strong, readwrite) UIBarButtonItem* dismissButton;
+@end
+
 @implementation TableContainerViewController
 @synthesize bottomToolbar = _bottomToolbar;
+@synthesize dismissButton = _dismissButton;
 @synthesize tableViewController = _tableViewController;
 
 #pragma mark - Public Interface
@@ -20,10 +28,25 @@
   self = [super initWithNibName:nil bundle:nil];
   if (self) {
     self.tableViewController = table;
+    // Dismiss Button configuration.
+    _dismissButton = [[UIBarButtonItem alloc] init];
+    _dismissButton.style = UIBarButtonItemStylePlain;
+    _dismissButton.title =
+        l10n_util::GetNSString(IDS_IOS_NAVIGATION_BAR_DONE_BUTTON);
   }
   return self;
 }
 
+#pragma mark Setters and Getters
+
+// TODO(crbug.com/805178): Temporary Toolbar code for prototyping purposes.
+- (void)setBottomToolbar:(UIView*)bottomToolbar {
+  _bottomToolbar = bottomToolbar;
+  _bottomToolbar.backgroundColor = [UIColor grayColor];
+  _bottomToolbar.translatesAutoresizingMaskIntoConstraints = NO;
+  _bottomToolbar.alpha = 0.5;
+}
+
 #pragma mark - View Lifecycle
 
 - (void)viewDidLoad {
@@ -46,14 +69,13 @@
   [self.view addSubview:self.tableViewController.view];
   [self.tableViewController didMoveToParentViewController:self];
 
-  // Bottom Toolbar
-  self.bottomToolbar = [[UIView alloc] initWithFrame:CGRectZero];
-  self.bottomToolbar.backgroundColor = [UIColor grayColor];
-  self.bottomToolbar.translatesAutoresizingMaskIntoConstraints = NO;
-  self.bottomToolbar.alpha = 0.5;
-  // Add the Bottom Toolbar after the TableView since it needs to be on top of
-  // the view hierarchy.
-  [self.view addSubview:self.bottomToolbar];
+  // Add Bottom Toolbar to view hierarchy if it exists.
+  if (self.bottomToolbar) {
+    // Add the Bottom Toolbar after the TableView since it needs to be on top of
+    // the view hierarchy.
+    [self.view addSubview:_bottomToolbar];
+    [self setUpBottomToolbarConstraints];
+  }
 
   [self setUpConstraints];
 }
@@ -70,6 +92,12 @@
         constraintEqualToAnchor:self.view.leadingAnchor],
     [self.tableViewController.view.trailingAnchor
         constraintEqualToAnchor:self.view.trailingAnchor],
+  ];
+  [NSLayoutConstraint activateConstraints:constraints];
+}
+
+- (void)setUpBottomToolbarConstraints {
+  NSArray* constraints = @[
     [self.bottomToolbar.leadingAnchor
         constraintEqualToAnchor:self.view.leadingAnchor],
     [self.bottomToolbar.trailingAnchor
@@ -78,7 +106,6 @@
         constraintEqualToAnchor:self.view.bottomAnchor],
     [self.bottomToolbar.heightAnchor constraintEqualToConstant:40],
   ];
-
   [NSLayoutConstraint activateConstraints:constraints];
 }
 
diff --git a/ios/public/provider/chrome/browser/user_feedback/user_feedback_provider.h b/ios/public/provider/chrome/browser/user_feedback/user_feedback_provider.h
index ae17219d1..8318c69 100644
--- a/ios/public/provider/chrome/browser/user_feedback/user_feedback_provider.h
+++ b/ios/public/provider/chrome/browser/user_feedback/user_feedback_provider.h
@@ -29,7 +29,7 @@
 - (UIImage*)currentPageScreenshot;
 
 // Returns the username of the account being synced.
-// Returns nil if sync is not enabled.
+// Returns nil if sync is not enabled or user is in incognito mode.
 - (NSString*)currentPageSyncedUserName;
 
 @end
diff --git a/ios/showcase/tab_grid/sc_grid_cell_view_controller.mm b/ios/showcase/tab_grid/sc_grid_cell_view_controller.mm
index 6d1e67c..899a47d 100644
--- a/ios/showcase/tab_grid/sc_grid_cell_view_controller.mm
+++ b/ios/showcase/tab_grid/sc_grid_cell_view_controller.mm
@@ -7,7 +7,8 @@
 #import "base/mac/foundation_util.h"
 #import "base/numerics/safe_conversions.h"
 #import "ios/chrome/browser/ui/tab_grid/grid_cell.h"
-#import "ios/chrome/browser/ui/tab_grid/grid_item.h"
+#import "ios/chrome/browser/ui/tab_grid/grid_theme.h"
+#import "ios/showcase/common/protocol_alerter.h"
 
 #if !defined(__has_feature) || !__has_feature(objc_arc)
 #error "This file requires ARC support."
@@ -18,35 +19,34 @@
 }  // namespace
 
 @interface SCGridCellViewController ()<UICollectionViewDataSource,
-                                       UICollectionViewDelegateFlowLayout>
-@property(nonatomic, strong) NSArray* items;
+                                       UICollectionViewDelegate>
 @property(nonatomic, strong) NSArray* sizes;
+@property(nonatomic, strong) NSIndexPath* selectedIndexPath;
+@property(nonatomic, strong) ProtocolAlerter* alerter;
 @end
 
 @implementation SCGridCellViewController
-@synthesize items = _items;
 @synthesize sizes = _sizes;
+@synthesize selectedIndexPath = _selectedIndexPath;
+@synthesize alerter = _alerter;
 
 - (instancetype)init {
   UICollectionViewFlowLayout* layout =
       [[UICollectionViewFlowLayout alloc] init];
   layout.sectionInset = UIEdgeInsetsMake(20.0f, 20.0f, 20.0f, 20.0f);
+  layout.minimumInteritemSpacing = 15.0f;
+  layout.minimumLineSpacing = 15.0f;
   if (self = [super initWithCollectionViewLayout:layout]) {
+    _alerter = [[ProtocolAlerter alloc]
+        initWithProtocols:@[ @protocol(GridCellDelegate) ]];
+    self.alerter.baseViewController = self;
     self.collectionView.dataSource = self;
     self.collectionView.delegate = self;
+    self.collectionView.allowsMultipleSelection = YES;
     [self.collectionView registerClass:[GridCell class]
             forCellWithReuseIdentifier:kCellIdentifier];
     self.collectionView.backgroundColor = [UIColor blackColor];
-    GridItem* item1 = [[GridItem alloc] init];
-    item1.title = @"Basecamp: project with an attitude.";
-    GridItem* item2 = [[GridItem alloc] init];
-    item2.title = @"Basecamp: project with an attitude.";
-    GridItem* item3 = [[GridItem alloc] init];
-    item3.title = @"Basecamp: project with an attitude.";
-    GridItem* item4 = [[GridItem alloc] init];
-    item4.title = @"Basecamp: project with an attitude.";
-    self.items = @[ item1, item2, item3, item4 ];
-    self.sizes = @[
+    _sizes = @[
       [NSValue valueWithCGSize:CGSizeMake(140.0f, 168.0f)],
       [NSValue valueWithCGSize:CGSizeMake(180.0f, 208.0f)],
       [NSValue valueWithCGSize:CGSizeMake(220.0f, 248.0f)],
@@ -60,7 +60,7 @@
 
 - (NSInteger)collectionView:(UICollectionView*)collectionView
      numberOfItemsInSection:(NSInteger)section {
-  return base::checked_cast<NSInteger>(self.items.count);
+  return base::checked_cast<NSInteger>(self.sizes.count);
 }
 
 - (UICollectionViewCell*)collectionView:(UICollectionView*)collectionView
@@ -68,10 +68,14 @@
   GridCell* cell = base::mac::ObjCCastStrict<GridCell>([collectionView
       dequeueReusableCellWithReuseIdentifier:kCellIdentifier
                                 forIndexPath:indexPath]);
-  GridCellTheme theme = GridCellThemeLight;
+  cell.delegate = static_cast<id<GridCellDelegate>>(self.alerter);
   if (indexPath.item == 1)
-    theme = GridCellThemeDark;
-  [cell configureWithItem:self.items[indexPath.item] theme:theme];
+    cell.theme = GridThemeDark;
+  else
+    cell.theme = GridThemeLight;
+  cell.title = @"YouTube - Trending videos";
+  cell.icon = [UIImage imageNamed:@"Icon-180"];
+  cell.snapshot = [UIImage imageNamed:@"Sample-screenshot-portrait"];
   return cell;
 }
 
@@ -81,4 +85,12 @@
   return [self.sizes[indexPath.item] CGSizeValue];
 }
 
+#pragma mark - UICollectionViewDelegate
+
+- (void)collectionView:(UICollectionView*)collectionView
+    didSelectItemAtIndexPath:(NSIndexPath*)indexPath {
+  [collectionView deselectItemAtIndexPath:self.selectedIndexPath animated:NO];
+  self.selectedIndexPath = indexPath;
+}
+
 @end
diff --git a/ios/showcase/table_view/sc_table_container_coordinator.mm b/ios/showcase/table_view/sc_table_container_coordinator.mm
index 018660b..e9fd423 100644
--- a/ios/showcase/table_view/sc_table_container_coordinator.mm
+++ b/ios/showcase/table_view/sc_table_container_coordinator.mm
@@ -24,6 +24,7 @@
       initWithTable:[[ChromeTableViewController alloc]
                         initWithStyle:UITableViewStylePlain]];
   self.viewController.title = @"Table View";
+  self.viewController.bottomToolbar = [[UIView alloc] initWithFrame:CGRectZero];
   [self.baseViewController pushViewController:self.viewController animated:YES];
 }
 
diff --git a/ios/web_view/BUILD.gn b/ios/web_view/BUILD.gn
index 6819c7b..eb15b8d 100644
--- a/ios/web_view/BUILD.gn
+++ b/ios/web_view/BUILD.gn
@@ -261,6 +261,8 @@
     "internal/cwv_html_element_unittest.mm",
     "internal/cwv_preferences_unittest.mm",
     "internal/cwv_preview_element_info_unittest.mm",
+    "internal/translate/cwv_translation_language_unittest.mm",
+    "internal/translate/cwv_translation_policy_unittest.mm",
     "internal/web_view_web_client_unittest.mm",
   ]
   sources += ios_web_view_sources
diff --git a/ios/web_view/internal/translate/cwv_translation_language_unittest.mm b/ios/web_view/internal/translate/cwv_translation_language_unittest.mm
new file mode 100644
index 0000000..29247050
--- /dev/null
+++ b/ios/web_view/internal/translate/cwv_translation_language_unittest.mm
@@ -0,0 +1,37 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#import "ios/web_view/internal/translate/cwv_translation_language_internal.h"
+
+#import <Foundation/Foundation.h>
+
+#include "base/strings/sys_string_conversions.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#import "testing/gtest_mac.h"
+#include "testing/platform_test.h"
+
+#if !defined(__has_feature) || !__has_feature(objc_arc)
+#error "This file requires ARC support."
+#endif
+
+namespace ios_web_view {
+
+using CWVTranslationLanguageTest = PlatformTest;
+
+// Tests CWVTranslationLanguage initialization.
+TEST_F(CWVTranslationLanguageTest, Initialization) {
+  NSString* language_code = @"ja";
+  NSString* localized_name = @"Japanese";
+  NSString* native_name = @"日本語";
+  CWVTranslationLanguage* language = [[CWVTranslationLanguage alloc]
+      initWithLanguageCode:base::SysNSStringToUTF8(language_code)
+             localizedName:base::SysNSStringToUTF16(localized_name)
+                nativeName:base::SysNSStringToUTF16(native_name)];
+
+  EXPECT_NSEQ(language_code, language.languageCode);
+  EXPECT_NSEQ(localized_name, language.localizedName);
+  EXPECT_NSEQ(native_name, language.nativeName);
+}
+
+}  // namespace ios_web_view
diff --git a/ios/web_view/internal/translate/cwv_translation_policy_unittest.mm b/ios/web_view/internal/translate/cwv_translation_policy_unittest.mm
new file mode 100644
index 0000000..5d9c654b
--- /dev/null
+++ b/ios/web_view/internal/translate/cwv_translation_policy_unittest.mm
@@ -0,0 +1,43 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#import "ios/web_view/public/cwv_translation_policy.h"
+
+#import <Foundation/Foundation.h>
+
+#include "base/strings/sys_string_conversions.h"
+#import "ios/web_view/internal/translate/cwv_translation_language_internal.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#import "testing/gtest_mac.h"
+#include "testing/platform_test.h"
+
+#if !defined(__has_feature) || !__has_feature(objc_arc)
+#error "This file requires ARC support."
+#endif
+
+namespace ios_web_view {
+
+using CWVTranslationPolicyTest = PlatformTest;
+
+// Tests CWVTranslationPolicy initialization.
+TEST_F(CWVTranslationPolicyTest, Initialization) {
+  CWVTranslationPolicy* ask_policy =
+      [CWVTranslationPolicy translationPolicyAsk];
+  EXPECT_EQ(CWVTranslationPolicyAsk, ask_policy.type);
+
+  CWVTranslationPolicy* never_policy =
+      [CWVTranslationPolicy translationPolicyNever];
+  EXPECT_EQ(CWVTranslationPolicyNever, never_policy.type);
+
+  CWVTranslationLanguage* language = [[CWVTranslationLanguage alloc]
+      initWithLanguageCode:base::SysNSStringToUTF8(@"ja")
+             localizedName:base::SysNSStringToUTF16(@"Japanese")
+                nativeName:base::SysNSStringToUTF16(@"日本語")];
+  CWVTranslationPolicy* auto_policy =
+      [CWVTranslationPolicy translationPolicyAutoTranslateToLanguage:language];
+  EXPECT_EQ(CWVTranslationPolicyAuto, auto_policy.type);
+  EXPECT_NSEQ(language, auto_policy.language);
+}
+
+}  // namespace ios_web_view
diff --git a/ipc/ipc_mojo_bootstrap.cc b/ipc/ipc_mojo_bootstrap.cc
index 6b0fd3b..cb86bda 100644
--- a/ipc/ipc_mojo_bootstrap.cc
+++ b/ipc/ipc_mojo_bootstrap.cc
@@ -587,8 +587,15 @@
     if (task_runner_->BelongsToCurrentThread()) {
       DCHECK(thread_checker_.CalledOnValidThread());
       if (!connector_ || paused_) {
-        if (!shut_down_)
+        if (!shut_down_) {
           outgoing_messages_.emplace_back(std::move(*message));
+
+          // TODO(https://crbug.com/813045): Remove this. Typically this queue
+          // won't exceed something like 50 messages even on slow devices. If
+          // the massive leaks we see can be attributed to this queue, it would
+          // have to be quite a bit larger.
+          CHECK_LE(outgoing_messages_.size(), 100000u);
+        }
         return true;
       }
       return connector_->Accept(message);
diff --git a/mash/test/mash_test_suite.cc b/mash/test/mash_test_suite.cc
index f9eacd0..a5160d5 100644
--- a/mash/test/mash_test_suite.cc
+++ b/mash/test/mash_test_suite.cc
@@ -8,6 +8,7 @@
 
 #include "ash/public/cpp/config.h"
 #include "ash/test/ash_test_helper.h"
+#include "base/base_switches.h"
 #include "base/command_line.h"
 #include "base/files/file_path.h"
 #include "base/path_service.h"
@@ -16,6 +17,7 @@
 #include "components/viz/service/surfaces/surface_manager.h"
 #include "ui/aura/env.h"
 #include "ui/base/resource/resource_bundle.h"
+#include "ui/base/ui_base_features.h"
 #include "ui/base/ui_base_paths.h"
 #include "ui/base/ui_base_switches.h"
 #include "ui/compositor/compositor.h"
@@ -38,9 +40,9 @@
 
   base::CommandLine::ForCurrentProcess()->AppendSwitch(
       switches::kOverrideUseSoftwareGLForTests);
-  base::CommandLine::ForCurrentProcess()->AppendSwitch(switches::kMus);
-  base::CommandLine::ForCurrentProcess()->AppendSwitch(
-      switches::kMusHostingViz);
+  base::CommandLine::ForCurrentProcess()->AppendSwitchASCII(
+      switches::kEnableFeatures, features::kMash.name);
+  feature_list_.InitAndEnableFeature(features::kMash);
 
   // Load ash mus strings and resources; not 'common' (Chrome) resources.
   base::FilePath resources;
diff --git a/mash/test/mash_test_suite.h b/mash/test/mash_test_suite.h
index 719958a..1aa55644 100644
--- a/mash/test/mash_test_suite.h
+++ b/mash/test/mash_test_suite.h
@@ -36,6 +36,7 @@
   base::TestDiscardableMemoryAllocator discardable_memory_allocator_;
   std::unique_ptr<aura::Env> env_;
   std::unique_ptr<ui::FakeContextFactory> context_factory_;
+  base::test::ScopedFeatureList feature_list_;
 
   DISALLOW_COPY_AND_ASSIGN(MashTestSuite);
 };
diff --git a/media/base/media_switches.cc b/media/base/media_switches.cc
index 3161ad9..67adad84 100644
--- a/media/base/media_switches.cc
+++ b/media/base/media_switches.cc
@@ -234,9 +234,7 @@
     "MemoryPressureBasedSourceBufferGC", base::FEATURE_DISABLED_BY_DEFAULT};
 
 // On systems where pepper CDMs are enabled, use mojo CDM instead of PPAPI CDM.
-// Note that mojo CDM support is still under development. Some features are
-// still missing and this feature should only be enabled for testing.
-const base::Feature kMojoCdm{"MojoCdm", base::FEATURE_DISABLED_BY_DEFAULT};
+const base::Feature kMojoCdm{"MojoCdm", base::FEATURE_ENABLED_BY_DEFAULT};
 
 // Enable MojoVideoDecoder.  Has no effect except on Android currently.
 const base::Feature kMojoVideoDecoder{"MojoVideoDecoder",
diff --git a/media/blink/video_frame_compositor.cc b/media/blink/video_frame_compositor.cc
index e352adf7..d0ed58f6 100644
--- a/media/blink/video_frame_compositor.cc
+++ b/media/blink/video_frame_compositor.cc
@@ -43,8 +43,6 @@
       last_interval_(base::TimeDelta::FromSecondsD(1.0 / 60)),
       callback_(nullptr),
       submitter_(std::move(submitter)),
-      surface_layer_for_video_enabled_(
-          base::FeatureList::IsEnabled(media::kUseSurfaceLayerForVideo)),
       weak_ptr_factory_(this) {
   background_rendering_timer_.SetTaskRunner(task_runner_);
   if (submitter_.get()) {
diff --git a/media/blink/video_frame_compositor.h b/media/blink/video_frame_compositor.h
index b3d83d0..d86ad95 100644
--- a/media/blink/video_frame_compositor.h
+++ b/media/blink/video_frame_compositor.h
@@ -214,9 +214,6 @@
   std::unique_ptr<base::trace_event::AutoOpenCloseEvent> auto_open_close_;
   std::unique_ptr<blink::WebVideoFrameSubmitter> submitter_;
 
-  // Whether the use of a surface layer instead of a video layer is enabled.
-  bool surface_layer_for_video_enabled_ = false;
-
   base::WeakPtrFactory<VideoFrameCompositor> weak_ptr_factory_;
 
   DISALLOW_COPY_AND_ASSIGN(VideoFrameCompositor);
diff --git a/media/blink/webmediaplayer_impl.cc b/media/blink/webmediaplayer_impl.cc
index d25e151d..3bedc49 100644
--- a/media/blink/webmediaplayer_impl.cc
+++ b/media/blink/webmediaplayer_impl.cc
@@ -260,8 +260,7 @@
           params->enable_instant_source_buffer_gc()),
       embedded_media_experience_enabled_(
           params->embedded_media_experience_enabled()),
-      surface_layer_for_video_enabled_(
-          base::FeatureList::IsEnabled(media::kUseSurfaceLayerForVideo)),
+      surface_layer_for_video_enabled_(params->use_surface_layer_for_video()),
       request_routing_token_cb_(params->request_routing_token_cb()),
       overlay_routing_token_(OverlayInfo::RoutingToken()),
       media_metrics_provider_(params->take_metrics_provider()) {
diff --git a/media/blink/webmediaplayer_impl_unittest.cc b/media/blink/webmediaplayer_impl_unittest.cc
index 21a570a7..0201018e 100644
--- a/media/blink/webmediaplayer_impl_unittest.cc
+++ b/media/blink/webmediaplayer_impl_unittest.cc
@@ -306,7 +306,8 @@
         std::move(provider),
         base::Bind(&WebMediaPlayerImplTest::CreateMockSurfaceLayerBridge,
                    base::Unretained(this)),
-        cc::TestContextProvider::Create());
+        cc::TestContextProvider::Create(),
+        base::FeatureList::IsEnabled(media::kUseSurfaceLayerForVideo));
 
     auto compositor = std::make_unique<StrictMock<MockVideoFrameCompositor>>(
         params->video_frame_compositor_task_runner());
diff --git a/media/blink/webmediaplayer_params.cc b/media/blink/webmediaplayer_params.cc
index ea8f342d..5b378e6 100644
--- a/media/blink/webmediaplayer_params.cc
+++ b/media/blink/webmediaplayer_params.cc
@@ -31,7 +31,8 @@
     mojom::MediaMetricsProviderPtr metrics_provider,
     base::Callback<std::unique_ptr<blink::WebSurfaceLayerBridge>(
         blink::WebSurfaceLayerBridgeObserver*)> create_bridge_callback,
-    scoped_refptr<viz::ContextProvider> context_provider)
+    scoped_refptr<viz::ContextProvider> context_provider,
+    bool use_surface_layer_for_video)
     : defer_load_cb_(defer_load_cb),
       audio_renderer_sink_(audio_renderer_sink),
       media_log_(std::move(media_log)),
@@ -52,7 +53,8 @@
       embedded_media_experience_enabled_(embedded_media_experience_enabled),
       metrics_provider_(std::move(metrics_provider)),
       create_bridge_callback_(create_bridge_callback),
-      context_provider_(std::move(context_provider)) {}
+      context_provider_(std::move(context_provider)),
+      use_surface_layer_for_video_(use_surface_layer_for_video) {}
 
 WebMediaPlayerParams::~WebMediaPlayerParams() = default;
 
diff --git a/media/blink/webmediaplayer_params.h b/media/blink/webmediaplayer_params.h
index 1256f638..0fb1ba37 100644
--- a/media/blink/webmediaplayer_params.h
+++ b/media/blink/webmediaplayer_params.h
@@ -82,7 +82,8 @@
       mojom::MediaMetricsProviderPtr metrics_provider,
       base::Callback<std::unique_ptr<blink::WebSurfaceLayerBridge>(
           blink::WebSurfaceLayerBridgeObserver*)> bridge_callback,
-      scoped_refptr<viz::ContextProvider> context_provider);
+      scoped_refptr<viz::ContextProvider> context_provider,
+      bool use_surface_layer_for_video);
 
   ~WebMediaPlayerParams();
 
@@ -161,6 +162,10 @@
     return context_provider_;
   }
 
+  bool use_surface_layer_for_video() const {
+    return use_surface_layer_for_video_;
+  }
+
  private:
   DeferLoadCB defer_load_cb_;
   scoped_refptr<SwitchableAudioRendererSink> audio_renderer_sink_;
@@ -185,6 +190,7 @@
       blink::WebSurfaceLayerBridgeObserver*)>
       create_bridge_callback_;
   scoped_refptr<viz::ContextProvider> context_provider_;
+  bool use_surface_layer_for_video_;
 
   DISALLOW_IMPLICIT_CONSTRUCTORS(WebMediaPlayerParams);
 };
diff --git a/media/filters/chunk_demuxer.cc b/media/filters/chunk_demuxer.cc
index 63af329..70c5593 100644
--- a/media/filters/chunk_demuxer.cc
+++ b/media/filters/chunk_demuxer.cc
@@ -649,6 +649,10 @@
   if ((state_ != WAITING_FOR_INIT && state_ != INITIALIZING) || IsValidId(id))
     return kReachedIdLimit;
 
+  // TODO(wolenetz): Change to DCHECK once less verification in release build is
+  // needed. See https://crbug.com/786975.
+  CHECK(!init_cb_.is_null());
+
   std::vector<std::string> parsed_codec_ids;
   media::SplitCodecsToVector(codecs, &parsed_codec_ids, false);
 
@@ -700,7 +704,11 @@
       base::Bind(&ChunkDemuxer::OnSourceInitDone, base::Unretained(this), id),
       expected_sbs_codecs, encrypted_media_init_data_cb_, new_text_track_cb);
 
+  // TODO(wolenetz): Change to DCHECKs once less verification in release build
+  // is needed. See https://crbug.com/786975.
+  CHECK(!IsValidId(id));
   source_state_map_[id] = std::move(source_state);
+  CHECK(IsValidId(id));
   return kOk;
 }
 
@@ -1145,6 +1153,12 @@
   lock_.AssertAcquired();
   DVLOG(1) << "ChunkDemuxer::ChangeState_Locked() : "
            << state_ << " -> " << new_state;
+
+  // TODO(wolenetz): Change to DCHECK once less verification in release build is
+  // needed. See https://crbug.com/786975.
+  // Disallow changes from at or beyond PARSE_ERROR to below PARSE_ERROR.
+  CHECK(!(state_ >= PARSE_ERROR && new_state < PARSE_ERROR));
+
   state_ = new_state;
 }
 
@@ -1199,11 +1213,15 @@
 
   // TODO(wolenetz): Change these to DCHECKs once less verification in release
   // build is needed. See https://crbug.com/786975.
-  const bool is_initializing = state_ == INITIALIZING;
-  const bool init_cb_is_set = !init_cb_.is_null();
-  const bool id_is_pending = pending_source_init_ids_.find(source_id) !=
-                             pending_source_init_ids_.end();
-  CHECK(is_initializing && init_cb_is_set && id_is_pending);
+  bool is_initializing = state_ == INITIALIZING;
+  bool init_cb_is_set = !init_cb_.is_null();
+  bool id_is_pending = pending_source_init_ids_.find(source_id) !=
+                       pending_source_init_ids_.end();
+  CHECK(!pending_source_init_ids_.empty());
+  CHECK(IsValidId(source_id));
+  CHECK(id_is_pending);
+  CHECK(init_cb_is_set);
+  CHECK(is_initializing);
   if (audio_streams_.empty() && video_streams_.empty()) {
     ReportError_Locked(DEMUXER_ERROR_COULD_NOT_OPEN);
     return;
diff --git a/media/filters/chunk_demuxer.h b/media/filters/chunk_demuxer.h
index 3bb24c2..d3fa05c 100644
--- a/media/filters/chunk_demuxer.h
+++ b/media/filters/chunk_demuxer.h
@@ -346,10 +346,13 @@
 
  private:
   enum State {
-    WAITING_FOR_INIT,
+    WAITING_FOR_INIT = 0,
     INITIALIZING,
     INITIALIZED,
     ENDED,
+
+    // Any State at or beyond PARSE_ERROR cannot be changed to a state before
+    // this. See ChangeState_Locked.
     PARSE_ERROR,
     SHUTDOWN,
   };
diff --git a/media/filters/chunk_demuxer_unittest.cc b/media/filters/chunk_demuxer_unittest.cc
index a7fe831..1e10c1e 100644
--- a/media/filters/chunk_demuxer_unittest.cc
+++ b/media/filters/chunk_demuxer_unittest.cc
@@ -2545,6 +2545,12 @@
 // Test that removing an ID immediately after adding it does not interfere with
 // quota for new IDs in the future.
 TEST_P(ChunkDemuxerTest, RemoveAndAddId) {
+  demuxer_->Initialize(
+      &host_,
+      base::BindRepeating(&ChunkDemuxerTest::DemuxerInitialized,
+                          base::Unretained(this)),
+      true);
+
   std::string audio_id_1 = "audio1";
   ASSERT_TRUE(AddId(audio_id_1, HAS_AUDIO) == ChunkDemuxer::kOk);
   demuxer_->RemoveId(audio_id_1);
@@ -3104,6 +3110,11 @@
 }
 
 TEST_P(ChunkDemuxerTest, CodecPrefixMatching) {
+  demuxer_->Initialize(
+      &host_,
+      base::BindRepeating(&ChunkDemuxerTest::DemuxerInitialized,
+                          base::Unretained(this)),
+      true);
   ChunkDemuxer::Status expected = ChunkDemuxer::kNotSupported;
 
 #if BUILDFLAG(USE_PROPRIETARY_CODECS)
@@ -3133,6 +3144,12 @@
     "mp4a.40.05"
   };
 
+  demuxer_->Initialize(
+      &host_,
+      base::BindRepeating(&ChunkDemuxerTest::DemuxerInitialized,
+                          base::Unretained(this)),
+      true);
+
   for (size_t i = 0; i < arraysize(codec_ids); ++i) {
 #if BUILDFLAG(USE_PROPRIETARY_CODECS)
     expected = ChunkDemuxer::kOk;
@@ -4886,6 +4903,11 @@
 }
 
 TEST_P(ChunkDemuxerTest, Mp4Vp9CodecSupport) {
+  demuxer_->Initialize(
+      &host_,
+      base::BindRepeating(&ChunkDemuxerTest::DemuxerInitialized,
+                          base::Unretained(this)),
+      true);
   ChunkDemuxer::Status expected = ChunkDemuxer::kOk;
   EXPECT_EQ(AddId("source_id", "video/mp4", "vp09.00.10.08"), expected);
 }
diff --git a/media/gpu/h264_decoder.cc b/media/gpu/h264_decoder.cc
index dc9fe5d..d628906 100644
--- a/media/gpu/h264_decoder.cc
+++ b/media/gpu/h264_decoder.cc
@@ -20,13 +20,13 @@
 
 H264Decoder::H264Accelerator::~H264Accelerator() = default;
 
-H264Decoder::H264Decoder(H264Accelerator* accelerator)
+H264Decoder::H264Decoder(std::unique_ptr<H264Accelerator> accelerator)
     : state_(kNeedStreamMetadata),
       max_frame_num_(0),
       max_pic_num_(0),
       max_long_term_frame_idx_(0),
       max_num_reorder_frames_(0),
-      accelerator_(accelerator) {
+      accelerator_(std::move(accelerator)) {
   DCHECK(accelerator_);
   Reset();
 }
diff --git a/media/gpu/h264_decoder.h b/media/gpu/h264_decoder.h
index 545498e..87ced3b 100644
--- a/media/gpu/h264_decoder.h
+++ b/media/gpu/h264_decoder.h
@@ -101,7 +101,7 @@
     DISALLOW_COPY_AND_ASSIGN(H264Accelerator);
   };
 
-  H264Decoder(H264Accelerator* accelerator);
+  explicit H264Decoder(std::unique_ptr<H264Accelerator> accelerator);
   ~H264Decoder() override;
 
   // AcceleratedVideoDecoder implementation.
@@ -273,7 +273,7 @@
   // PicOrderCount of the previously outputted frame.
   int last_output_poc_;
 
-  H264Accelerator* accelerator_;
+  const std::unique_ptr<H264Accelerator> accelerator_;
 
   DISALLOW_COPY_AND_ASSIGN(H264Decoder);
 };
diff --git a/media/gpu/h264_decoder_unittest.cc b/media/gpu/h264_decoder_unittest.cc
index 1d1e31d38..48047f5 100644
--- a/media/gpu/h264_decoder_unittest.cc
+++ b/media/gpu/h264_decoder_unittest.cc
@@ -90,7 +90,7 @@
 
  protected:
   std::unique_ptr<H264Decoder> decoder_;
-  MockH264Accelerator accelerator_;
+  MockH264Accelerator* accelerator_;
 
  private:
   base::queue<std::string> input_frame_files_;
@@ -98,14 +98,16 @@
 };
 
 void H264DecoderTest::SetUp() {
-  decoder_.reset(new H264Decoder(&accelerator_));
+  auto mock_accelerator = std::make_unique<MockH264Accelerator>();
+  accelerator_ = mock_accelerator.get();
+  decoder_.reset(new H264Decoder(std::move(mock_accelerator)));
 
   // Sets default behaviors for mock methods for convenience.
-  ON_CALL(accelerator_, CreateH264Picture()).WillByDefault(Invoke([]() {
+  ON_CALL(*accelerator_, CreateH264Picture()).WillByDefault(Invoke([]() {
     return new H264Picture();
   }));
-  ON_CALL(accelerator_, SubmitDecode(_)).WillByDefault(Return(true));
-  ON_CALL(accelerator_, OutputPicture(_)).WillByDefault(Return(true));
+  ON_CALL(*accelerator_, SubmitDecode(_)).WillByDefault(Return(true));
+  ON_CALL(*accelerator_, OutputPicture(_)).WillByDefault(Return(true));
 }
 
 void H264DecoderTest::SetInputFrameFiles(
@@ -162,15 +164,15 @@
   EXPECT_EQ(gfx::Size(320, 192), decoder_->GetPicSize());
   EXPECT_LE(9u, decoder_->GetRequiredNumOfPictures());
 
-  EXPECT_CALL(accelerator_, CreateH264Picture()).WillOnce(Return(nullptr));
+  EXPECT_CALL(*accelerator_, CreateH264Picture()).WillOnce(Return(nullptr));
   ASSERT_EQ(AcceleratedVideoDecoder::kRanOutOfSurfaces, Decode());
-  ASSERT_TRUE(Mock::VerifyAndClearExpectations(&accelerator_));
+  ASSERT_TRUE(Mock::VerifyAndClearExpectations(&*accelerator_));
 
   {
     InSequence sequence;
-    EXPECT_CALL(accelerator_, CreateH264Picture());
-    EXPECT_CALL(accelerator_, SubmitDecode(_));
-    EXPECT_CALL(accelerator_, OutputPicture(_));
+    EXPECT_CALL(*accelerator_, CreateH264Picture());
+    EXPECT_CALL(*accelerator_, SubmitDecode(_));
+    EXPECT_CALL(*accelerator_, OutputPicture(_));
   }
   ASSERT_EQ(AcceleratedVideoDecoder::kRanOutOfStreamData, Decode());
   ASSERT_TRUE(decoder_->Flush());
@@ -183,9 +185,9 @@
   EXPECT_LE(9u, decoder_->GetRequiredNumOfPictures());
   {
     InSequence sequence;
-    EXPECT_CALL(accelerator_, CreateH264Picture());
-    EXPECT_CALL(accelerator_, SubmitDecode(_));
-    EXPECT_CALL(accelerator_, OutputPicture(WithPoc(0)));
+    EXPECT_CALL(*accelerator_, CreateH264Picture());
+    EXPECT_CALL(*accelerator_, SubmitDecode(_));
+    EXPECT_CALL(*accelerator_, OutputPicture(WithPoc(0)));
   }
   ASSERT_EQ(AcceleratedVideoDecoder::kRanOutOfStreamData, Decode());
   ASSERT_TRUE(decoder_->Flush());
@@ -199,21 +201,21 @@
   EXPECT_EQ(gfx::Size(320, 192), decoder_->GetPicSize());
   EXPECT_LE(9u, decoder_->GetRequiredNumOfPictures());
 
-  EXPECT_CALL(accelerator_, CreateH264Picture()).Times(4);
+  EXPECT_CALL(*accelerator_, CreateH264Picture()).Times(4);
   Expectation decode_poc0, decode_poc2, decode_poc4, decode_poc6;
   {
     InSequence decode_order;
-    decode_poc0 = EXPECT_CALL(accelerator_, SubmitDecode(WithPoc(0)));
-    decode_poc2 = EXPECT_CALL(accelerator_, SubmitDecode(WithPoc(2)));
-    decode_poc4 = EXPECT_CALL(accelerator_, SubmitDecode(WithPoc(4)));
-    decode_poc6 = EXPECT_CALL(accelerator_, SubmitDecode(WithPoc(6)));
+    decode_poc0 = EXPECT_CALL(*accelerator_, SubmitDecode(WithPoc(0)));
+    decode_poc2 = EXPECT_CALL(*accelerator_, SubmitDecode(WithPoc(2)));
+    decode_poc4 = EXPECT_CALL(*accelerator_, SubmitDecode(WithPoc(4)));
+    decode_poc6 = EXPECT_CALL(*accelerator_, SubmitDecode(WithPoc(6)));
   }
   {
     InSequence display_order;
-    EXPECT_CALL(accelerator_, OutputPicture(WithPoc(0))).After(decode_poc0);
-    EXPECT_CALL(accelerator_, OutputPicture(WithPoc(2))).After(decode_poc2);
-    EXPECT_CALL(accelerator_, OutputPicture(WithPoc(4))).After(decode_poc4);
-    EXPECT_CALL(accelerator_, OutputPicture(WithPoc(6))).After(decode_poc6);
+    EXPECT_CALL(*accelerator_, OutputPicture(WithPoc(0))).After(decode_poc0);
+    EXPECT_CALL(*accelerator_, OutputPicture(WithPoc(2))).After(decode_poc2);
+    EXPECT_CALL(*accelerator_, OutputPicture(WithPoc(4))).After(decode_poc4);
+    EXPECT_CALL(*accelerator_, OutputPicture(WithPoc(6))).After(decode_poc6);
   }
   ASSERT_EQ(AcceleratedVideoDecoder::kRanOutOfStreamData, Decode());
   ASSERT_TRUE(decoder_->Flush());
@@ -227,21 +229,21 @@
 
   // Two pictures will be kept in DPB for reordering. The first picture should
   // be outputted after feeding the third frame.
-  EXPECT_CALL(accelerator_, CreateH264Picture()).Times(4);
+  EXPECT_CALL(*accelerator_, CreateH264Picture()).Times(4);
   Expectation decode_poc0, decode_poc2, decode_poc4, decode_poc6;
   {
     InSequence decode_order;
-    decode_poc0 = EXPECT_CALL(accelerator_, SubmitDecode(WithPoc(0)));
-    decode_poc4 = EXPECT_CALL(accelerator_, SubmitDecode(WithPoc(4)));
-    decode_poc2 = EXPECT_CALL(accelerator_, SubmitDecode(WithPoc(2)));
-    decode_poc6 = EXPECT_CALL(accelerator_, SubmitDecode(WithPoc(6)));
+    decode_poc0 = EXPECT_CALL(*accelerator_, SubmitDecode(WithPoc(0)));
+    decode_poc4 = EXPECT_CALL(*accelerator_, SubmitDecode(WithPoc(4)));
+    decode_poc2 = EXPECT_CALL(*accelerator_, SubmitDecode(WithPoc(2)));
+    decode_poc6 = EXPECT_CALL(*accelerator_, SubmitDecode(WithPoc(6)));
   }
   {
     InSequence display_order;
-    EXPECT_CALL(accelerator_, OutputPicture(WithPoc(0))).After(decode_poc0);
-    EXPECT_CALL(accelerator_, OutputPicture(WithPoc(2))).After(decode_poc2);
-    EXPECT_CALL(accelerator_, OutputPicture(WithPoc(4))).After(decode_poc4);
-    EXPECT_CALL(accelerator_, OutputPicture(WithPoc(6))).After(decode_poc6);
+    EXPECT_CALL(*accelerator_, OutputPicture(WithPoc(0))).After(decode_poc0);
+    EXPECT_CALL(*accelerator_, OutputPicture(WithPoc(2))).After(decode_poc2);
+    EXPECT_CALL(*accelerator_, OutputPicture(WithPoc(4))).After(decode_poc4);
+    EXPECT_CALL(*accelerator_, OutputPicture(WithPoc(6))).After(decode_poc6);
   }
   ASSERT_EQ(AcceleratedVideoDecoder::kRanOutOfStreamData, Decode());
   ASSERT_TRUE(decoder_->Flush());
@@ -255,33 +257,33 @@
   EXPECT_EQ(gfx::Size(320, 192), decoder_->GetPicSize());
   EXPECT_LE(9u, decoder_->GetRequiredNumOfPictures());
 
-  EXPECT_CALL(accelerator_, CreateH264Picture());
+  EXPECT_CALL(*accelerator_, CreateH264Picture());
   {
     InSequence sequence;
-    EXPECT_CALL(accelerator_, SubmitDecode(_));
-    EXPECT_CALL(accelerator_, OutputPicture(WithPoc(0)));
+    EXPECT_CALL(*accelerator_, SubmitDecode(_));
+    EXPECT_CALL(*accelerator_, OutputPicture(WithPoc(0)));
   }
   ASSERT_EQ(AcceleratedVideoDecoder::kAllocateNewSurfaces, Decode());
   EXPECT_EQ(gfx::Size(320, 192), decoder_->GetPicSize());
   EXPECT_LE(16u, decoder_->GetRequiredNumOfPictures());
 
-  ASSERT_TRUE(Mock::VerifyAndClearExpectations(&accelerator_));
+  ASSERT_TRUE(Mock::VerifyAndClearExpectations(&*accelerator_));
 
-  EXPECT_CALL(accelerator_, CreateH264Picture()).Times(4);
+  EXPECT_CALL(*accelerator_, CreateH264Picture()).Times(4);
   Expectation decode_poc0, decode_poc2, decode_poc4, decode_poc6;
   {
     InSequence decode_order;
-    decode_poc0 = EXPECT_CALL(accelerator_, SubmitDecode(WithPoc(0)));
-    decode_poc4 = EXPECT_CALL(accelerator_, SubmitDecode(WithPoc(4)));
-    decode_poc2 = EXPECT_CALL(accelerator_, SubmitDecode(WithPoc(2)));
-    decode_poc6 = EXPECT_CALL(accelerator_, SubmitDecode(WithPoc(6)));
+    decode_poc0 = EXPECT_CALL(*accelerator_, SubmitDecode(WithPoc(0)));
+    decode_poc4 = EXPECT_CALL(*accelerator_, SubmitDecode(WithPoc(4)));
+    decode_poc2 = EXPECT_CALL(*accelerator_, SubmitDecode(WithPoc(2)));
+    decode_poc6 = EXPECT_CALL(*accelerator_, SubmitDecode(WithPoc(6)));
   }
   {
     InSequence display_order;
-    EXPECT_CALL(accelerator_, OutputPicture(WithPoc(0))).After(decode_poc0);
-    EXPECT_CALL(accelerator_, OutputPicture(WithPoc(2))).After(decode_poc2);
-    EXPECT_CALL(accelerator_, OutputPicture(WithPoc(4))).After(decode_poc4);
-    EXPECT_CALL(accelerator_, OutputPicture(WithPoc(6))).After(decode_poc6);
+    EXPECT_CALL(*accelerator_, OutputPicture(WithPoc(0))).After(decode_poc0);
+    EXPECT_CALL(*accelerator_, OutputPicture(WithPoc(2))).After(decode_poc2);
+    EXPECT_CALL(*accelerator_, OutputPicture(WithPoc(4))).After(decode_poc4);
+    EXPECT_CALL(*accelerator_, OutputPicture(WithPoc(6))).After(decode_poc6);
   }
   ASSERT_EQ(AcceleratedVideoDecoder::kRanOutOfStreamData, Decode());
   ASSERT_TRUE(decoder_->Flush());
@@ -296,33 +298,33 @@
   EXPECT_EQ(gfx::Size(320, 192), decoder_->GetPicSize());
   EXPECT_LE(16u, decoder_->GetRequiredNumOfPictures());
 
-  EXPECT_CALL(accelerator_, CreateH264Picture());
+  EXPECT_CALL(*accelerator_, CreateH264Picture());
   {
     InSequence sequence;
-    EXPECT_CALL(accelerator_, SubmitDecode(_));
-    EXPECT_CALL(accelerator_, OutputPicture(WithPoc(0)));
+    EXPECT_CALL(*accelerator_, SubmitDecode(_));
+    EXPECT_CALL(*accelerator_, OutputPicture(WithPoc(0)));
   }
   ASSERT_EQ(AcceleratedVideoDecoder::kAllocateNewSurfaces, Decode());
   EXPECT_EQ(gfx::Size(320, 192), decoder_->GetPicSize());
   EXPECT_LE(9u, decoder_->GetRequiredNumOfPictures());
 
-  ASSERT_TRUE(Mock::VerifyAndClearExpectations(&accelerator_));
+  ASSERT_TRUE(Mock::VerifyAndClearExpectations(&*accelerator_));
 
-  EXPECT_CALL(accelerator_, CreateH264Picture()).Times(4);
+  EXPECT_CALL(*accelerator_, CreateH264Picture()).Times(4);
   Expectation decode_poc0, decode_poc2, decode_poc4, decode_poc6;
   {
     InSequence decode_order;
-    decode_poc0 = EXPECT_CALL(accelerator_, SubmitDecode(WithPoc(0)));
-    decode_poc2 = EXPECT_CALL(accelerator_, SubmitDecode(WithPoc(2)));
-    decode_poc4 = EXPECT_CALL(accelerator_, SubmitDecode(WithPoc(4)));
-    decode_poc6 = EXPECT_CALL(accelerator_, SubmitDecode(WithPoc(6)));
+    decode_poc0 = EXPECT_CALL(*accelerator_, SubmitDecode(WithPoc(0)));
+    decode_poc2 = EXPECT_CALL(*accelerator_, SubmitDecode(WithPoc(2)));
+    decode_poc4 = EXPECT_CALL(*accelerator_, SubmitDecode(WithPoc(4)));
+    decode_poc6 = EXPECT_CALL(*accelerator_, SubmitDecode(WithPoc(6)));
   }
   {
     InSequence display_order;
-    EXPECT_CALL(accelerator_, OutputPicture(WithPoc(0))).After(decode_poc0);
-    EXPECT_CALL(accelerator_, OutputPicture(WithPoc(2))).After(decode_poc2);
-    EXPECT_CALL(accelerator_, OutputPicture(WithPoc(4))).After(decode_poc4);
-    EXPECT_CALL(accelerator_, OutputPicture(WithPoc(6))).After(decode_poc6);
+    EXPECT_CALL(*accelerator_, OutputPicture(WithPoc(0))).After(decode_poc0);
+    EXPECT_CALL(*accelerator_, OutputPicture(WithPoc(2))).After(decode_poc2);
+    EXPECT_CALL(*accelerator_, OutputPicture(WithPoc(4))).After(decode_poc4);
+    EXPECT_CALL(*accelerator_, OutputPicture(WithPoc(6))).After(decode_poc6);
   }
   ASSERT_EQ(AcceleratedVideoDecoder::kRanOutOfStreamData, Decode());
   ASSERT_TRUE(decoder_->Flush());
diff --git a/media/gpu/v4l2/v4l2_slice_video_decode_accelerator.cc b/media/gpu/v4l2/v4l2_slice_video_decode_accelerator.cc
index c4eda5c..9f0596bb 100644
--- a/media/gpu/v4l2/v4l2_slice_video_decode_accelerator.cc
+++ b/media/gpu/v4l2/v4l2_slice_video_decode_accelerator.cc
@@ -576,18 +576,16 @@
   }
 
   if (video_profile_ >= H264PROFILE_MIN && video_profile_ <= H264PROFILE_MAX) {
-    h264_accelerator_.reset(new V4L2H264Accelerator(this));
-    decoder_.reset(new H264Decoder(h264_accelerator_.get()));
+    decoder_.reset(
+        new H264Decoder(std::make_unique<V4L2H264Accelerator>(this)));
   } else if (video_profile_ >= VP8PROFILE_MIN &&
              video_profile_ <= VP8PROFILE_MAX) {
-    vp8_accelerator_.reset(new V4L2VP8Accelerator(this));
-    decoder_.reset(new VP8Decoder(vp8_accelerator_.get()));
+    decoder_.reset(new VP8Decoder(std::make_unique<V4L2VP8Accelerator>(this)));
   } else if (video_profile_ >= VP9PROFILE_MIN &&
              video_profile_ <= VP9PROFILE_MAX) {
-    vp9_accelerator_.reset(new V4L2VP9Accelerator(this));
-    decoder_.reset(new VP9Decoder(vp9_accelerator_.get()));
+    decoder_.reset(new VP9Decoder(std::make_unique<V4L2VP9Accelerator>(this)));
   } else {
-    NOTREACHED() << "Unsupported profile " << video_profile_;
+    NOTREACHED() << "Unsupported profile " << GetProfileName(video_profile_);
     return false;
   }
 
diff --git a/media/gpu/v4l2/v4l2_slice_video_decode_accelerator.h b/media/gpu/v4l2/v4l2_slice_video_decode_accelerator.h
index d76b4ba8..7ca2e35 100644
--- a/media/gpu/v4l2/v4l2_slice_video_decode_accelerator.h
+++ b/media/gpu/v4l2/v4l2_slice_video_decode_accelerator.h
@@ -431,12 +431,6 @@
   bool decoder_resetting_;
   bool surface_set_change_pending_;
 
-  // Hardware accelerators.
-  // TODO(posciak): Try to have a superclass here if possible.
-  std::unique_ptr<V4L2H264Accelerator> h264_accelerator_;
-  std::unique_ptr<V4L2VP8Accelerator> vp8_accelerator_;
-  std::unique_ptr<V4L2VP9Accelerator> vp9_accelerator_;
-
   // Codec-specific software decoder in use.
   std::unique_ptr<AcceleratedVideoDecoder> decoder_;
 
diff --git a/media/gpu/vaapi/vaapi_h264_accelerator.cc b/media/gpu/vaapi/vaapi_h264_accelerator.cc
index 15cda09..2406eca3 100644
--- a/media/gpu/vaapi/vaapi_h264_accelerator.cc
+++ b/media/gpu/vaapi/vaapi_h264_accelerator.cc
@@ -55,7 +55,7 @@
 
 VaapiH264Accelerator::VaapiH264Accelerator(
     VaapiVideoDecodeAccelerator* vaapi_dec,
-    VaapiWrapper* vaapi_wrapper)
+    scoped_refptr<VaapiWrapper> vaapi_wrapper)
     : vaapi_wrapper_(vaapi_wrapper), vaapi_dec_(vaapi_dec) {
   DCHECK(vaapi_wrapper_);
   DCHECK(vaapi_dec_);
diff --git a/media/gpu/vaapi/vaapi_h264_accelerator.h b/media/gpu/vaapi/vaapi_h264_accelerator.h
index 16a95cbb..c473069 100644
--- a/media/gpu/vaapi/vaapi_h264_accelerator.h
+++ b/media/gpu/vaapi/vaapi_h264_accelerator.h
@@ -23,7 +23,7 @@
     : public H264Decoder::H264Accelerator {
  public:
   VaapiH264Accelerator(VaapiVideoDecodeAccelerator* vaapi_dec,
-                       VaapiWrapper* vaapi_wrapper);
+                       const scoped_refptr<VaapiWrapper> vaapi_wrapper);
   ~VaapiH264Accelerator() override;
 
   // H264Decoder::H264Accelerator implementation.
@@ -55,7 +55,7 @@
                              VAPictureH264* va_pics,
                              int num_pics);
 
-  VaapiWrapper* vaapi_wrapper_;
+  const scoped_refptr<VaapiWrapper> vaapi_wrapper_;
   VaapiVideoDecodeAccelerator* vaapi_dec_;
 
   SEQUENCE_CHECKER(sequence_checker_);
diff --git a/media/gpu/vaapi/vaapi_video_decode_accelerator.cc b/media/gpu/vaapi/vaapi_video_decode_accelerator.cc
index 555bd3f..8836565d 100644
--- a/media/gpu/vaapi/vaapi_video_decode_accelerator.cc
+++ b/media/gpu/vaapi/vaapi_video_decode_accelerator.cc
@@ -182,15 +182,14 @@
   }
 
   if (profile >= H264PROFILE_MIN && profile <= H264PROFILE_MAX) {
-    h264_accelerator_.reset(
-        new VaapiH264Accelerator(this, vaapi_wrapper_.get()));
-    decoder_.reset(new H264Decoder(h264_accelerator_.get()));
+    decoder_.reset(new H264Decoder(
+        std::make_unique<VaapiH264Accelerator>(this, vaapi_wrapper_.get())));
   } else if (profile >= VP8PROFILE_MIN && profile <= VP8PROFILE_MAX) {
-    vp8_accelerator_.reset(new VaapiVP8Accelerator(this, vaapi_wrapper_.get()));
-    decoder_.reset(new VP8Decoder(vp8_accelerator_.get()));
+    decoder_.reset(new VP8Decoder(
+        std::make_unique<VaapiVP8Accelerator>(this, vaapi_wrapper_)));
   } else if (profile >= VP9PROFILE_MIN && profile <= VP9PROFILE_MAX) {
-    vp9_accelerator_.reset(new VaapiVP9Accelerator(this, vaapi_wrapper_.get()));
-    decoder_.reset(new VP9Decoder(vp9_accelerator_.get()));
+    decoder_.reset(new VP9Decoder(
+        std::make_unique<VaapiVP9Accelerator>(this, vaapi_wrapper_.get())));
   } else {
     VLOGF(1) << "Unsupported profile " << GetProfileName(profile);
     return false;
@@ -847,7 +846,7 @@
   client_ptr_factory_.reset();
   weak_this_factory_.InvalidateWeakPtrs();
 
-  // TODO(mcasas): consider deleting |decoder_| and |*_accelerator_| on
+  // TODO(mcasas): consider deleting |decoder_| on
   // |decoder_thread_task_runner_|, https://crbug.com/789160.
 
   // Signal all potential waiters on the decoder_thread_, let them early-exit,
diff --git a/media/gpu/vaapi/vaapi_video_decode_accelerator.h b/media/gpu/vaapi/vaapi_video_decode_accelerator.h
index 2277acc5..a7b7184 100644
--- a/media/gpu/vaapi/vaapi_video_decode_accelerator.h
+++ b/media/gpu/vaapi/vaapi_video_decode_accelerator.h
@@ -44,9 +44,6 @@
 class AcceleratedVideoDecoder;
 class VaapiDecodeSurface;
 class VaapiPicture;
-class VaapiH264Accelerator;
-class VaapiVP8Accelerator;
-class VaapiVP9Accelerator;
 
 // Class to provide video decode acceleration for Intel systems with hardware
 // support for it, and on which libva is available.
@@ -224,7 +221,9 @@
 
   std::unique_ptr<VaapiPictureFactory> vaapi_picture_factory_;
 
+  // Constructed in Initialize() when the codec information is received.
   scoped_refptr<VaapiWrapper> vaapi_wrapper_;
+  std::unique_ptr<AcceleratedVideoDecoder> decoder_;
 
   // All allocated Pictures, regardless of their current state. Pictures are
   // allocated once using |create_vaapi_picture_callback_| and destroyed at the
@@ -274,13 +273,6 @@
   std::unique_ptr<base::WeakPtrFactory<Client>> client_ptr_factory_;
   base::WeakPtr<Client> client_;
 
-  // Accelerators come after vaapi_wrapper_ to ensure they are destroyed first.
-  std::unique_ptr<VaapiH264Accelerator> h264_accelerator_;
-  std::unique_ptr<VaapiVP8Accelerator> vp8_accelerator_;
-  std::unique_ptr<VaapiVP9Accelerator> vp9_accelerator_;
-  // After *_accelerator_ to ensure correct destruction order.
-  std::unique_ptr<AcceleratedVideoDecoder> decoder_;
-
   base::Thread decoder_thread_;
   // Use this to post tasks to |decoder_thread_| instead of
   // |decoder_thread_.message_loop()| because the latter will be NULL once
diff --git a/media/gpu/vaapi/vaapi_vp8_accelerator.cc b/media/gpu/vaapi/vaapi_vp8_accelerator.cc
index bc94611..d56a13f 100644
--- a/media/gpu/vaapi/vaapi_vp8_accelerator.cc
+++ b/media/gpu/vaapi/vaapi_vp8_accelerator.cc
@@ -34,8 +34,9 @@
   DISALLOW_COPY_AND_ASSIGN(VaapiVP8Picture);
 };
 
-VaapiVP8Accelerator::VaapiVP8Accelerator(VaapiVideoDecodeAccelerator* vaapi_dec,
-                                         VaapiWrapper* vaapi_wrapper)
+VaapiVP8Accelerator::VaapiVP8Accelerator(
+    VaapiVideoDecodeAccelerator* vaapi_dec,
+    scoped_refptr<VaapiWrapper> vaapi_wrapper)
     : vaapi_wrapper_(vaapi_wrapper), vaapi_dec_(vaapi_dec) {
   DCHECK(vaapi_wrapper_);
   DCHECK(vaapi_dec_);
diff --git a/media/gpu/vaapi/vaapi_vp8_accelerator.h b/media/gpu/vaapi/vaapi_vp8_accelerator.h
index 83a0a7d..7291a9e 100644
--- a/media/gpu/vaapi/vaapi_vp8_accelerator.h
+++ b/media/gpu/vaapi/vaapi_vp8_accelerator.h
@@ -20,7 +20,7 @@
 class MEDIA_GPU_EXPORT VaapiVP8Accelerator : public VP8Decoder::VP8Accelerator {
  public:
   VaapiVP8Accelerator(VaapiVideoDecodeAccelerator* vaapi_dec,
-                      VaapiWrapper* vaapi_wrapper);
+                      scoped_refptr<VaapiWrapper> vaapi_wrapper);
   ~VaapiVP8Accelerator() override;
 
   // VP8Decoder::VP8Accelerator implementation.
@@ -36,7 +36,7 @@
   scoped_refptr<VaapiDecodeSurface> VP8PictureToVaapiDecodeSurface(
       const scoped_refptr<VP8Picture>& pic);
 
-  VaapiWrapper* vaapi_wrapper_;
+  const scoped_refptr<VaapiWrapper> vaapi_wrapper_;
   VaapiVideoDecodeAccelerator* vaapi_dec_;
 
   SEQUENCE_CHECKER(sequence_checker_);
diff --git a/media/gpu/vaapi/vaapi_vp9_accelerator.cc b/media/gpu/vaapi/vaapi_vp9_accelerator.cc
index 6bd502c..5c385c13 100644
--- a/media/gpu/vaapi/vaapi_vp9_accelerator.cc
+++ b/media/gpu/vaapi/vaapi_vp9_accelerator.cc
@@ -34,8 +34,9 @@
   DISALLOW_COPY_AND_ASSIGN(VaapiVP9Picture);
 };
 
-VaapiVP9Accelerator::VaapiVP9Accelerator(VaapiVideoDecodeAccelerator* vaapi_dec,
-                                         VaapiWrapper* vaapi_wrapper)
+VaapiVP9Accelerator::VaapiVP9Accelerator(
+    VaapiVideoDecodeAccelerator* vaapi_dec,
+    scoped_refptr<VaapiWrapper> vaapi_wrapper)
     : vaapi_wrapper_(vaapi_wrapper), vaapi_dec_(vaapi_dec) {
   DCHECK(vaapi_wrapper_);
   DCHECK(vaapi_dec_);
diff --git a/media/gpu/vaapi/vaapi_vp9_accelerator.h b/media/gpu/vaapi/vaapi_vp9_accelerator.h
index d318430..2410902 100644
--- a/media/gpu/vaapi/vaapi_vp9_accelerator.h
+++ b/media/gpu/vaapi/vaapi_vp9_accelerator.h
@@ -20,7 +20,7 @@
 class MEDIA_GPU_EXPORT VaapiVP9Accelerator : public VP9Decoder::VP9Accelerator {
  public:
   VaapiVP9Accelerator(VaapiVideoDecodeAccelerator* vaapi_dec,
-                      VaapiWrapper* vaapi_wrapper);
+                      scoped_refptr<VaapiWrapper> vaapi_wrapper);
   ~VaapiVP9Accelerator() override;
 
   // VP9Decoder::VP9Accelerator implementation.
@@ -40,7 +40,7 @@
   scoped_refptr<VaapiDecodeSurface> VP9PictureToVaapiDecodeSurface(
       const scoped_refptr<VP9Picture>& pic);
 
-  VaapiWrapper* vaapi_wrapper_;
+  const scoped_refptr<VaapiWrapper> vaapi_wrapper_;
   VaapiVideoDecodeAccelerator* vaapi_dec_;
 
   SEQUENCE_CHECKER(sequence_checker_);
diff --git a/media/gpu/vp8_decoder.cc b/media/gpu/vp8_decoder.cc
index e7bdfc2e..0abe7dc 100644
--- a/media/gpu/vp8_decoder.cc
+++ b/media/gpu/vp8_decoder.cc
@@ -11,15 +11,15 @@
 
 VP8Decoder::VP8Accelerator::~VP8Accelerator() {}
 
-VP8Decoder::VP8Decoder(VP8Accelerator* accelerator)
+VP8Decoder::VP8Decoder(std::unique_ptr<VP8Accelerator> accelerator)
     : state_(kNeedStreamMetadata),
       curr_frame_start_(nullptr),
       frame_size_(0),
-      accelerator_(accelerator) {
+      accelerator_(std::move(accelerator)) {
   DCHECK(accelerator_);
 }
 
-VP8Decoder::~VP8Decoder() {}
+VP8Decoder::~VP8Decoder() = default;
 
 bool VP8Decoder::Flush() {
   DVLOG(2) << "Decoder flush";
diff --git a/media/gpu/vp8_decoder.h b/media/gpu/vp8_decoder.h
index 549271fb..d4564642 100644
--- a/media/gpu/vp8_decoder.h
+++ b/media/gpu/vp8_decoder.h
@@ -63,7 +63,7 @@
     DISALLOW_COPY_AND_ASSIGN(VP8Accelerator);
   };
 
-  VP8Decoder(VP8Accelerator* accelerator);
+  explicit VP8Decoder(std::unique_ptr<VP8Accelerator> accelerator);
   ~VP8Decoder() override;
 
   // AcceleratedVideoDecoder implementation.
@@ -102,7 +102,7 @@
   int horizontal_scale_;
   int vertical_scale_;
 
-  VP8Accelerator* accelerator_;
+  const std::unique_ptr<VP8Accelerator> accelerator_;
 
   DISALLOW_COPY_AND_ASSIGN(VP8Decoder);
 };
diff --git a/media/gpu/vp9_decoder.cc b/media/gpu/vp9_decoder.cc
index be7fd85..4c99f25 100644
--- a/media/gpu/vp9_decoder.cc
+++ b/media/gpu/vp9_decoder.cc
@@ -17,14 +17,14 @@
 
 VP9Decoder::VP9Accelerator::~VP9Accelerator() {}
 
-VP9Decoder::VP9Decoder(VP9Accelerator* accelerator)
+VP9Decoder::VP9Decoder(std::unique_ptr<VP9Accelerator> accelerator)
     : state_(kNeedStreamMetadata),
-      accelerator_(accelerator),
-      parser_(accelerator->IsFrameContextRequired()) {
+      accelerator_(std::move(accelerator)),
+      parser_(accelerator_->IsFrameContextRequired()) {
   ref_frames_.resize(kVp9NumRefFrames);
 }
 
-VP9Decoder::~VP9Decoder() {}
+VP9Decoder::~VP9Decoder() = default;
 
 void VP9Decoder::SetStream(const uint8_t* ptr, size_t size) {
   DCHECK(ptr);
diff --git a/media/gpu/vp9_decoder.h b/media/gpu/vp9_decoder.h
index d2264b59..0d8e15d 100644
--- a/media/gpu/vp9_decoder.h
+++ b/media/gpu/vp9_decoder.h
@@ -92,7 +92,7 @@
     DISALLOW_COPY_AND_ASSIGN(VP9Accelerator);
   };
 
-  explicit VP9Decoder(VP9Accelerator* accelerator);
+  explicit VP9Decoder(std::unique_ptr<VP9Accelerator> accelerator);
   ~VP9Decoder() override;
 
   // AcceleratedVideoDecoder implementation.
@@ -140,8 +140,7 @@
   // Current coded resolution.
   gfx::Size pic_size_;
 
-  // VP9Accelerator instance owned by the client.
-  VP9Accelerator* accelerator_;
+  const std::unique_ptr<VP9Accelerator> accelerator_;
 
   Vp9Parser parser_;
 
diff --git a/mojo/BUILD.gn b/mojo/BUILD.gn
index cfb2273..7bb5cce0 100644
--- a/mojo/BUILD.gn
+++ b/mojo/BUILD.gn
@@ -3,6 +3,7 @@
 # found in the LICENSE file.
 
 import("//build/config/ui.gni")
+import("//testing/test.gni")
 
 group("mojo") {
   # Meta-target, don't link into production code.
@@ -26,14 +27,22 @@
 group("tests") {
   testonly = true
   deps = [
+    ":mojo_unittests",
     "//ipc:ipc_tests",
     "//mojo/common:mojo_common_unittests",
     "//mojo/edk/system:mojo_message_pipe_perftests",
-    "//mojo/edk/system:mojo_system_unittests",
     "//mojo/edk/test:mojo_public_bindings_perftests",
-    "//mojo/edk/test:mojo_public_bindings_unittests",
     "//mojo/edk/test:mojo_public_system_perftests",
-    "//mojo/edk/test:mojo_public_system_unittests",
     "//services/service_manager/tests",
   ]
 }
+
+test("mojo_unittests") {
+  deps = [
+    "//mojo/edk/system:test_sources",
+    "//mojo/edk/test:run_all_unittests",
+    "//mojo/public/cpp/base:tests",
+    "//mojo/public/cpp/bindings/tests",
+    "//mojo/public/cpp/system/tests",
+  ]
+}
diff --git a/mojo/edk/embedder/BUILD.gn b/mojo/edk/embedder/BUILD.gn
index a69f6194..fa60f2f7 100644
--- a/mojo/edk/embedder/BUILD.gn
+++ b/mojo/edk/embedder/BUILD.gn
@@ -142,7 +142,7 @@
   testonly = true
 
   # TODO: Figure out why this visibility check fails on Android.
-  # visibility = [ "//mojo/edk/system:mojo_system_unittests" ]
+  # visibility = [ "//mojo:mojo_unittests" ]
 
   sources = [
     "embedder_unittest.cc",
diff --git a/mojo/edk/embedder/README.md b/mojo/edk/embedder/README.md
index 2e7c02d..b6e0a449 100644
--- a/mojo/edk/embedder/README.md
+++ b/mojo/edk/embedder/README.md
@@ -178,7 +178,7 @@
 
 Now you have IPC initialized between two processes. For some practical examples
 of how this is done, you can dig into the various multiprocess tests in the
-`mojo_system_unittests` test suite.
+`mojo_unittests` test suite.
 
 ## Bootstrapping Cross-Process Message Pipes
 
diff --git a/mojo/edk/system/BUILD.gn b/mojo/edk/system/BUILD.gn
index 8c23c50..864ee5b 100644
--- a/mojo/edk/system/BUILD.gn
+++ b/mojo/edk/system/BUILD.gn
@@ -119,17 +119,6 @@
   allow_circular_includes_from = [ "//mojo/edk/embedder" ]
 }
 
-group("tests") {
-  testonly = true
-  deps = [
-    ":mojo_system_unittests",
-  ]
-
-  if (!is_ios) {
-    deps += [ ":mojo_message_pipe_perftests" ]
-  }
-}
-
 source_set("test_utils") {
   testonly = true
 
@@ -151,7 +140,8 @@
   ]
 }
 
-test("mojo_system_unittests") {
+source_set("test_sources") {
+  testonly = true
   sources = [
     "channel_unittest.cc",
     "core_test_base.cc",
diff --git a/mojo/edk/system/channel_win.cc b/mojo/edk/system/channel_win.cc
index b2c5396..980f021 100644
--- a/mojo/edk/system/channel_win.cc
+++ b/mojo/edk/system/channel_win.cc
@@ -106,6 +106,14 @@
       bool write_now = !delay_writes_ && outgoing_messages_.empty();
       outgoing_messages_.emplace_back(std::move(message), 0);
 
+      // TODO(https://crbug.com/813045): Remove this. The queue should almost
+      // never be used, and then only for a handful of messages. For messages
+      // to accumulate, the sender would have to be sending faster than the
+      // receiver can read. This check is just to look for a message leak,
+      // though it may also reveal e.g. spammy malware sites exploiting some
+      // web APIs to DoS the browser.
+      CHECK_LE(outgoing_messages_.size(), 100000u);
+
       if (write_now && !WriteNoLock(outgoing_messages_.front()))
         reject_writes_ = write_error = true;
     }
diff --git a/mojo/edk/test/BUILD.gn b/mojo/edk/test/BUILD.gn
index e9987ad..1b805966 100644
--- a/mojo/edk/test/BUILD.gn
+++ b/mojo/edk/test/BUILD.gn
@@ -96,16 +96,6 @@
 # TODO(vtl): These don't really belong here. (They should be converted to
 # apptests, but even apart from that these targets belong somewhere else.)
 
-# TODO(https://crbug.com/799242): Merge the unit tests into one binary.
-group("public_tests") {
-  testonly = true
-  deps = [
-    ":mojo_public_bindings_unittests",
-    ":mojo_public_system_perftests",
-    ":mojo_public_system_unittests",
-  ]
-}
-
 test("mojo_public_bindings_perftests") {
   deps = [
     ":run_all_perftests",
@@ -114,25 +104,9 @@
   ]
 }
 
-test("mojo_public_bindings_unittests") {
-  deps = [
-    ":run_all_unittests",
-    "//mojo/edk/test:test_support",
-    "//mojo/public/cpp/base:tests",
-    "//mojo/public/cpp/bindings/tests",
-  ]
-}
-
 test("mojo_public_system_perftests") {
   deps = [
     ":run_all_perftests",
     "//mojo/public/c/system/tests:perftests",
   ]
 }
-
-test("mojo_public_system_unittests") {
-  deps = [
-    ":run_all_unittests",
-    "//mojo/public/cpp/system/tests",
-  ]
-}
diff --git a/mojo/public/c/system/tests/BUILD.gn b/mojo/public/c/system/tests/BUILD.gn
index bace63c4..f116cc6 100644
--- a/mojo/public/c/system/tests/BUILD.gn
+++ b/mojo/public/c/system/tests/BUILD.gn
@@ -6,12 +6,12 @@
   testonly = true
 
   visibility = [
-    "//mojo/public/cpp/system/tests:mojo_public_system_unittests",
+    "//mojo/public/cpp/system/tests:mojo_unittests",
     "//mojo/public/cpp/system/tests:tests",
   ]
 
   sources = [
-    "core_unittest.cc",
+    "core_api_unittest.cc",
     "core_unittest_pure_c.c",
     "macros_unittest.cc",
   ]
diff --git a/mojo/public/c/system/tests/core_unittest.cc b/mojo/public/c/system/tests/core_api_unittest.cc
similarity index 97%
rename from mojo/public/c/system/tests/core_unittest.cc
rename to mojo/public/c/system/tests/core_api_unittest.cc
index 7fa87ed..b9f50076 100644
--- a/mojo/public/c/system/tests/core_unittest.cc
+++ b/mojo/public/c/system/tests/core_api_unittest.cc
@@ -23,7 +23,7 @@
     MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_WRITABLE |
     MOJO_HANDLE_SIGNAL_PEER_CLOSED | MOJO_HANDLE_SIGNAL_PEER_REMOTE;
 
-TEST(CoreTest, GetTimeTicksNow) {
+TEST(CoreAPITest, GetTimeTicksNow) {
   const MojoTimeTicks start = MojoGetTimeTicksNow();
   EXPECT_NE(static_cast<MojoTimeTicks>(0), start)
       << "MojoGetTimeTicksNow should return nonzero value";
@@ -31,7 +31,7 @@
 
 // The only handle that's guaranteed to be invalid is |MOJO_HANDLE_INVALID|.
 // Tests that everything that takes a handle properly recognizes it.
-TEST(CoreTest, InvalidHandle) {
+TEST(CoreAPITest, InvalidHandle) {
   MojoHandle h0, h1;
   char buffer[10] = {0};
   uint32_t buffer_size;
@@ -75,7 +75,7 @@
             MojoMapBuffer(h0, 0, 1, &write_pointer, MOJO_MAP_BUFFER_FLAG_NONE));
 }
 
-TEST(CoreTest, BasicMessagePipe) {
+TEST(CoreAPITest, BasicMessagePipe) {
   MojoHandle h0, h1;
   MojoHandleSignals sig;
 
@@ -151,7 +151,7 @@
   EXPECT_EQ(MOJO_RESULT_OK, MojoClose(h1));
 }
 
-TEST(CoreTest, BasicDataPipe) {
+TEST(CoreAPITest, BasicDataPipe) {
   MojoHandle hp, hc;
   MojoHandleSignals sig;
   char buffer[20] = {0};
@@ -264,7 +264,7 @@
   // the producer never-writable?
 }
 
-TEST(CoreTest, BasicSharedBuffer) {
+TEST(CoreAPITest, BasicSharedBuffer) {
   MojoHandle h0, h1;
   void* pointer;
 
@@ -314,7 +314,7 @@
 extern "C" const char* MinimalCTest(void);
 
 // This checks that things actually work in C (not C++).
-TEST(CoreTest, MinimalCTest) {
+TEST(CoreAPITest, MinimalCTest) {
   const char* failure = MinimalCTest();
   EXPECT_FALSE(failure) << failure;
 }
diff --git a/net/BUILD.gn b/net/BUILD.gn
index 22c93189..0f2552ca 100644
--- a/net/BUILD.gn
+++ b/net/BUILD.gn
@@ -993,7 +993,6 @@
       "nqe/effective_connection_type_observer.h",
       "nqe/event_creator.cc",
       "nqe/event_creator.h",
-      "nqe/external_estimate_provider.h",
       "nqe/network_id.cc",
       "nqe/network_id.h",
       "nqe/network_qualities_prefs_manager.cc",
@@ -2189,6 +2188,8 @@
         "websockets/websocket_handshake_stream_base.h",
         "websockets/websocket_handshake_stream_create_helper.cc",
         "websockets/websocket_handshake_stream_create_helper.h",
+        "websockets/websocket_http2_handshake_stream.cc",
+        "websockets/websocket_http2_handshake_stream.h",
         "websockets/websocket_inflater.cc",
         "websockets/websocket_inflater.h",
         "websockets/websocket_stream.cc",
diff --git a/net/OWNERS b/net/OWNERS
index 6b3369d..28514a7 100644
--- a/net/OWNERS
+++ b/net/OWNERS
@@ -1,10 +1,8 @@
 agl@chromium.org
 asanka@chromium.org
 bnc@chromium.org
-cbentzel@chromium.org
 davidben@chromium.org
 eroman@chromium.org
-gavinp@chromium.org
 jkarlin@chromium.org
 juliatuttle@chromium.org
 mattm@chromium.org
diff --git a/net/disk_cache/simple/OWNERS b/net/disk_cache/simple/OWNERS
index 060bfc57..bce1299 100644
--- a/net/disk_cache/simple/OWNERS
+++ b/net/disk_cache/simple/OWNERS
@@ -1,4 +1,3 @@
-gavinp@chromium.org
 pasko@chromium.org
 
 # COMPONENT: Internals>Network>Cache>Simple
diff --git a/net/http/http_cache_unittest.cc b/net/http/http_cache_unittest.cc
index ce29994..29f47f7 100644
--- a/net/http/http_cache_unittest.cc
+++ b/net/http/http_cache_unittest.cc
@@ -646,6 +646,11 @@
       bool using_proxy) override {
     return nullptr;
   }
+  std::unique_ptr<WebSocketHandshakeStreamBase> CreateHttp2Stream(
+      base::WeakPtr<SpdySession> session) override {
+    NOTREACHED();
+    return nullptr;
+  }
 };
 
 // Returns true if |entry| is not one of the log types paid attention to in this
diff --git a/net/http/http_stream_factory_impl_job.cc b/net/http/http_stream_factory_impl_job.cc
index ba90be2..b249e8e 100644
--- a/net/http/http_stream_factory_impl_job.cc
+++ b/net/http/http_stream_factory_impl_job.cc
@@ -427,13 +427,16 @@
     return false;
   }
 
+  if (is_websocket_ && origin_url_.SchemeIs(url::kWssScheme) &&
+      session_->params().enable_websocket_over_http2) {
+    return true;
+  }
+
   // We need to make sure that if a spdy session was created for
   // https://somehost/ then we do not use that session for http://somehost:443/.
   // The only time we can use an existing session is if the request URL is
   // https (the normal case) or if we are connecting to a SPDY proxy.
   // https://crbug.com/133176
-  // TODO(bnc): Add kWssScheme back to this list when WebSockets over HTTP/2 is
-  // implemented.  https://crbug.com/801564.
   return origin_url_.SchemeIs(url::kHttpsScheme) ||
          proxy_info_.proxy_server().is_https();
 }
@@ -441,7 +444,7 @@
 void HttpStreamFactoryImpl::Job::OnStreamReadyCallback() {
   DCHECK(stream_.get());
   DCHECK_NE(job_type_, PRECONNECT);
-  DCHECK(!is_websocket_);
+  DCHECK(!is_websocket_ || session_->params().enable_websocket_over_http2);
 
   MaybeCopyConnectionAttemptsFromSocketOrHandle();
 
@@ -545,14 +548,14 @@
     SpdySessionPool* spdy_session_pool,
     const SpdySessionKey& spdy_session_key,
     bool enable_ip_based_pooling,
+    bool is_websocket,
     const AddressList& addresses,
     const NetLogWithSource& net_log) {
   // It is OK to dereference spdy_session_pool, because the
   // ClientSocketPoolManager will be destroyed in the same callback that
   // destroys the SpdySessionPool.
   return spdy_session_pool->FindAvailableSession(
-             spdy_session_key, enable_ip_based_pooling,
-             false /* is_websocket */, net_log)
+             spdy_session_key, enable_ip_based_pooling, is_websocket, net_log)
              ? ERR_SPDY_SESSION_ALREADY_EXISTS
              : OK;
 }
@@ -918,14 +921,16 @@
   // connection this request can pool to.  If so, then go straight to using
   // that.
   if (CanUseExistingSpdySession()) {
-    session_->spdy_session_pool()->push_promise_index()->ClaimPushedStream(
-        spdy_session_key_, origin_url_, request_info_, &existing_spdy_session_,
-        &pushed_stream_id_);
+    if (!is_websocket_) {
+      session_->spdy_session_pool()->push_promise_index()->ClaimPushedStream(
+          spdy_session_key_, origin_url_, request_info_,
+          &existing_spdy_session_, &pushed_stream_id_);
+    }
     if (!existing_spdy_session_) {
       existing_spdy_session_ =
           session_->spdy_session_pool()->FindAvailableSession(
-              spdy_session_key_, enable_ip_based_pooling_,
-              false /* is_websocket */, net_log_);
+              spdy_session_key_, enable_ip_based_pooling_, is_websocket_,
+              net_log_);
     }
     if (existing_spdy_session_) {
       // If we're preconnecting, but we already have a SpdySession, we don't
@@ -966,7 +971,8 @@
   OnHostResolutionCallback resolution_callback =
       CanUseExistingSpdySession()
           ? base::Bind(&Job::OnHostResolution, session_->spdy_session_pool(),
-                       spdy_session_key_, enable_ip_based_pooling_)
+                       spdy_session_key_, enable_ip_based_pooling_,
+                       is_websocket_)
           : OnHostResolutionCallback();
   if (is_websocket_) {
     DCHECK(request_info_.socket_tag == SocketTag());
@@ -1008,8 +1014,8 @@
     // probably an IP pooled connection.
     existing_spdy_session_ =
         session_->spdy_session_pool()->FindAvailableSession(
-            spdy_session_key_, enable_ip_based_pooling_,
-            false /* is_websocket */, net_log_);
+            spdy_session_key_, enable_ip_based_pooling_, is_websocket_,
+            net_log_);
     if (existing_spdy_session_) {
       using_spdy_ = true;
       next_state_ = STATE_CREATE_STREAM;
@@ -1144,10 +1150,17 @@
 
 int HttpStreamFactoryImpl::Job::SetSpdyHttpStreamOrBidirectionalStreamImpl(
     base::WeakPtr<SpdySession> session) {
-  // TODO(bnc): Restore the code for WebSockets over HTTP/2 once it is
-  // implemented.  https://crbug.com/801564.
-  if (is_websocket_)
+  DCHECK(using_spdy_);
+  if (is_websocket_ && !session_->params().enable_websocket_over_http2) {
     return ERR_NOT_IMPLEMENTED;
+  }
+  if (is_websocket_) {
+    DCHECK_NE(job_type_, PRECONNECT);
+    DCHECK(delegate_->websocket_handshake_stream_create_helper());
+    websocket_stream_ = delegate_->websocket_handshake_stream_create_helper()
+                            ->CreateHttp2Stream(session);
+    return OK;
+  }
   if (stream_type_ == HttpStreamRequest::BIDIRECTIONAL_STREAM) {
     bidirectional_stream_impl_ = std::make_unique<BidirectionalStreamSpdyImpl>(
         session, net_log_.source());
@@ -1210,8 +1223,8 @@
     if (!existing_spdy_session_) {
       existing_spdy_session_ =
           session_->spdy_session_pool()->FindAvailableSession(
-              spdy_session_key_, enable_ip_based_pooling_,
-              false /* is_websocket */, net_log_);
+              spdy_session_key_, enable_ip_based_pooling_, is_websocket_,
+              net_log_);
     }
   }
   if (existing_spdy_session_) {
diff --git a/net/http/http_stream_factory_impl_job.h b/net/http/http_stream_factory_impl_job.h
index 9565eddf..5600499 100644
--- a/net/http/http_stream_factory_impl_job.h
+++ b/net/http/http_stream_factory_impl_job.h
@@ -400,6 +400,7 @@
   static int OnHostResolution(SpdySessionPool* spdy_session_pool,
                               const SpdySessionKey& spdy_session_key,
                               bool enable_ip_based_pooling,
+                              bool is_websocket,
                               const AddressList& addresses,
                               const NetLogWithSource& net_log);
 
diff --git a/net/http/http_stream_factory_impl_job_controller.cc b/net/http/http_stream_factory_impl_job_controller.cc
index 7847ef3..14e494d 100644
--- a/net/http/http_stream_factory_impl_job_controller.cc
+++ b/net/http/http_stream_factory_impl_job_controller.cc
@@ -1294,36 +1294,28 @@
         proxy_info_.proxy_server().host_port_pair());
   }
 
-  HostPortPair destination(HostPortPair::FromURL(request_info_.url));
-  GURL origin_url = ApplyHostMappingRules(request_info_.url, &destination);
-
-  int rv = session_->proxy_resolution_service()->ReconsiderProxyAfterError(
-      origin_url, request_info_.method, error, &proxy_info_, io_callback_,
-      &proxy_resolve_request_, session_->context().proxy_delegate, net_log_);
-  if (rv == OK || rv == ERR_IO_PENDING) {
-    if (!job->using_quic())
-      RemoveRequestFromSpdySessionRequestMap();
-    // Abandon all Jobs and start over.
-    job_bound_ = false;
-    bound_job_ = nullptr;
-    alternative_job_.reset();
-    main_job_.reset();
-    // Also resets states that related to the old main job. In particular,
-    // cancels |resume_main_job_callback_| so there won't be any delayed
-    // ResumeMainJob() left in the task queue.
-    resume_main_job_callback_.Cancel();
-    main_job_is_resumed_ = false;
-    main_job_is_blocked_ = false;
-
-    next_state_ = STATE_RESOLVE_PROXY_COMPLETE;
-  } else {
-    // If ReconsiderProxyAfterError() failed synchronously, it means
-    // there was nothing left to fall-back to, so fail the transaction
+  if (!proxy_info_.Fallback(error, net_log_)) {
+    // If there is no more proxy to fallback to, fail the transaction
     // with the last connection error we got.
-    // TODO(eroman): This is a confusing contract, make it more obvious.
-    rv = error;
+    return error;
   }
-  return rv;
+
+  if (!job->using_quic())
+    RemoveRequestFromSpdySessionRequestMap();
+  // Abandon all Jobs and start over.
+  job_bound_ = false;
+  bound_job_ = nullptr;
+  alternative_job_.reset();
+  main_job_.reset();
+  // Also resets states that related to the old main job. In particular,
+  // cancels |resume_main_job_callback_| so there won't be any delayed
+  // ResumeMainJob() left in the task queue.
+  resume_main_job_callback_.Cancel();
+  main_job_is_resumed_ = false;
+  main_job_is_blocked_ = false;
+
+  next_state_ = STATE_RESOLVE_PROXY_COMPLETE;
+  return OK;
 }
 
 bool HttpStreamFactoryImpl::JobController::IsQuicWhitelistedForHost(
diff --git a/net/http/http_stream_factory_impl_job_controller_unittest.cc b/net/http/http_stream_factory_impl_job_controller_unittest.cc
index 3baf64cd..a89bb0e 100644
--- a/net/http/http_stream_factory_impl_job_controller_unittest.cc
+++ b/net/http/http_stream_factory_impl_job_controller_unittest.cc
@@ -628,155 +628,6 @@
   EXPECT_TRUE(tcp_data_2.AllWriteDataConsumed());
 }
 
-// Tests that the second main (HTTP) job is resumed after change in proxy
-// configuration. When the proxy configuration changes, the job controller
-// retries the previously failed jobs with the new configuration. Since there is
-// an alternative job, the first and the second main jobs are delayed. The test
-// verifies that the jobs are resumed after the alternative jobs failed.
-// The result (OK) of the second main job should be returned to the delegate.
-// Regression test for crbug.com/787148.
-TEST_F(JobControllerReconsiderProxyAfterErrorTest,
-       SecondMainJobIsResumedAfterProxyConfigChange) {
-  // Initialize the test with direct connection.
-  std::unique_ptr<ProxyResolutionService> proxy_resolution_service =
-      ProxyResolutionService::CreateDirect();
-  ProxyResolutionService* proxy_resolution_service_raw = proxy_resolution_service.get();
-  session_deps_.proxy_resolution_service = std::move(proxy_resolution_service);
-
-  // Create a request.
-  HttpRequestInfo request_info;
-  request_info.method = "GET";
-  request_info.url = GURL("https://www.example.com:443");
-
-  HttpStreamFactoryImplJobControllerTest::Initialize(request_info);
-
-  // Add QUIC hint.
-  AlternativeService alternative_service(kProtoQUIC, "www.example.com", 443);
-  SetAlternativeService(request_info, alternative_service);
-
-  // Enable delayed TCP and set time delay for waiting job.
-  QuicStreamFactory* quic_stream_factory = session_->quic_stream_factory();
-  quic_stream_factory->set_require_confirmation(false);
-  ServerNetworkStats stats1;
-  stats1.srtt = base::TimeDelta::FromSeconds(100);
-  session_->http_server_properties()->SetServerNetworkStats(
-      url::SchemeHostPort(GURL("https://www.example.com:443")), stats1);
-
-  // Prepare the mocked data.
-  crypto_client_stream_factory_.set_handshake_mode(
-      MockCryptoClientStream::COLD_START);
-  MockQuicData quic_data_1;
-  quic_data_1.AddRead(ASYNC, ERR_QUIC_PROTOCOL_ERROR);
-  quic_data_1.AddSocketDataToFactory(session_deps_.socket_factory.get());
-
-  StaticSocketDataProvider tcp_data_1;
-  tcp_data_1.set_connect_data(MockConnect(SYNCHRONOUS, ERR_CONNECTION_REFUSED));
-  session_deps_.socket_factory->AddSocketDataProvider(&tcp_data_1);
-
-  MockQuicData quic_data_2;
-  quic_data_2.AddRead(ASYNC, ERR_QUIC_PROTOCOL_ERROR);
-  quic_data_2.AddSocketDataToFactory(session_deps_.socket_factory.get());
-
-  StaticSocketDataProvider tcp_data_2;
-  tcp_data_2.set_connect_data(MockConnect(SYNCHRONOUS, OK));
-  session_deps_.socket_factory->AddSocketDataProvider(&tcp_data_2);
-  SSLSocketDataProvider ssl_data(SYNCHRONOUS, OK);
-  session_deps_.socket_factory->AddSSLSocketDataProvider(&ssl_data);
-
-  EXPECT_CALL(request_delegate_, OnStreamReadyImpl(_, _, _)).Times(1);
-  EXPECT_CALL(request_delegate_, OnStreamFailed(_, _, _)).Times(0);
-
-  // Create the job controller.
-  std::unique_ptr<HttpStreamRequest> request =
-      CreateJobController(request_info);
-
-  // Calling ForceReloadProxyConfig will cause the proxy configuration to
-  // change. It will still be the direct connection but the configuration
-  // version will be bumped. That is enough for the job controller to restart
-  // the jobs.
-  proxy_resolution_service_raw->ForceReloadProxyConfig();
-
-  base::RunLoop().RunUntilIdle();
-
-  EXPECT_TRUE(quic_data_1.AllReadDataConsumed());
-  EXPECT_TRUE(quic_data_1.AllWriteDataConsumed());
-  EXPECT_TRUE(quic_data_2.AllReadDataConsumed());
-  EXPECT_TRUE(quic_data_2.AllWriteDataConsumed());
-  EXPECT_TRUE(tcp_data_1.AllReadDataConsumed());
-  EXPECT_TRUE(tcp_data_1.AllWriteDataConsumed());
-  EXPECT_TRUE(tcp_data_2.AllReadDataConsumed());
-  EXPECT_TRUE(tcp_data_2.AllWriteDataConsumed());
-}
-
-// Regression test for crbug.com/723589.
-TEST_P(JobControllerReconsiderProxyAfterErrorTest,
-       ProxyResolutionSucceedsOnReconsiderAsync) {
-  const int mock_error = ::testing::get<1>(GetParam());
-  StaticSocketDataProvider failed_main_job;
-  failed_main_job.set_connect_data(MockConnect(ASYNC, mock_error));
-  session_deps_.socket_factory->AddSocketDataProvider(&failed_main_job);
-
-  StaticSocketDataProvider successful_fallback;
-  successful_fallback.set_connect_data(MockConnect(SYNCHRONOUS, OK));
-  session_deps_.socket_factory->AddSocketDataProvider(&successful_fallback);
-
-  ProxyConfig proxy_config;
-  GURL pac_url("http://fooproxyurl/old.pac");
-  proxy_config.set_pac_url(pac_url);
-  proxy_config.set_pac_mandatory(true);
-  MockAsyncProxyResolverFactory* proxy_resolver_factory =
-      new MockAsyncProxyResolverFactory(false);
-  ProxyResolutionService* proxy_resolution_service = new ProxyResolutionService(
-      std::make_unique<ProxyConfigServiceFixed>(proxy_config),
-      base::WrapUnique(proxy_resolver_factory), nullptr);
-  HttpRequestInfo request_info;
-  request_info.method = "GET";
-  request_info.url = GURL("http://www.example.com");
-
-  Initialize(base::WrapUnique<ProxyResolutionService>(proxy_resolution_service), nullptr);
-  std::unique_ptr<HttpStreamRequest> request =
-      CreateJobController(request_info);
-  ASSERT_EQ(1u, proxy_resolver_factory->pending_requests().size());
-
-  EXPECT_CALL(request_delegate_, OnStreamFailed(_, _, _)).Times(0);
-  EXPECT_EQ(
-      pac_url,
-      proxy_resolver_factory->pending_requests()[0]->script_data()->url());
-  MockAsyncProxyResolver resolver;
-  proxy_resolver_factory->pending_requests()[0]->CompleteNowWithForwarder(
-      OK, &resolver);
-  ASSERT_EQ(1u, resolver.pending_jobs().size());
-  EXPECT_EQ(request_info.url, resolver.pending_jobs()[0]->url());
-  resolver.pending_jobs()[0]->results()->UsePacString("PROXY badproxy:10");
-  resolver.pending_jobs()[0]->CompleteNow(OK);
-  ASSERT_EQ(0u, proxy_resolver_factory->pending_requests().size());
-
-  ProxyConfig new_proxy_config;
-  GURL new_pac_url("http://fooproxyurl/new.pac");
-  new_proxy_config.set_pac_url(new_pac_url);
-  new_proxy_config.set_pac_mandatory(true);
-  auto new_proxy_config_service =
-      std::make_unique<ProxyConfigServiceFixed>(new_proxy_config);
-  proxy_resolution_service->ResetConfigService(std::move(new_proxy_config_service));
-  base::RunLoop().RunUntilIdle();
-  ASSERT_EQ(1u, proxy_resolver_factory->pending_requests().size());
-  EXPECT_EQ(
-      new_pac_url,
-      proxy_resolver_factory->pending_requests()[0]->script_data()->url());
-  proxy_resolver_factory->pending_requests()[0]->CompleteNowWithForwarder(
-      OK, &resolver);
-  ASSERT_EQ(1u, resolver.pending_jobs().size());
-  EXPECT_EQ(request_info.url, resolver.pending_jobs()[0]->url());
-  resolver.pending_jobs()[0]->results()->UsePacString(
-      "PROXY goodfallbackproxy:80");
-  resolver.pending_jobs()[0]->CompleteNow(OK);
-
-  EXPECT_CALL(request_delegate_, OnStreamReadyImpl(_, _, _));
-  base::RunLoop().RunUntilIdle();
-  request.reset();
-  EXPECT_TRUE(HttpStreamFactoryImplPeer::IsJobControllerDeleted(factory_));
-}
-
 TEST_F(HttpStreamFactoryImplJobControllerTest,
        OnStreamFailedWithNoAlternativeJob) {
   tcp_data_ = std::make_unique<SequencedSocketData>(nullptr, 0, nullptr, 0);
diff --git a/net/http/http_stream_factory_impl_unittest.cc b/net/http/http_stream_factory_impl_unittest.cc
index 002070b..6b85b16d 100644
--- a/net/http/http_stream_factory_impl_unittest.cc
+++ b/net/http/http_stream_factory_impl_unittest.cc
@@ -336,6 +336,11 @@
     return std::make_unique<WebSocketBasicHandshakeStream>(
         std::move(connection));
   }
+  std::unique_ptr<WebSocketHandshakeStreamBase> CreateHttp2Stream(
+      base::WeakPtr<SpdySession> session) override {
+    NOTREACHED();
+    return nullptr;
+  }
 };
 
 struct TestCase {
diff --git a/net/http/http_stream_parser.cc b/net/http/http_stream_parser.cc
index dd23c898..7e7d1df2 100644
--- a/net/http/http_stream_parser.cc
+++ b/net/http/http_stream_parser.cc
@@ -4,6 +4,7 @@
 
 #include "net/http/http_stream_parser.h"
 
+#include <algorithm>
 #include <utility>
 
 #include "base/bind.h"
@@ -867,7 +868,7 @@
   DCHECK_LE(read_buf_->offset(), read_buf_->capacity());
   DCHECK_GT(result, 0);
 
-  int end_of_header_offset = FindAndParseResponseHeaders();
+  int end_of_header_offset = FindAndParseResponseHeaders(result);
 
   // Note: -1 is special, it indicates we haven't found the end of headers.
   // Anything less than -1 is a net::Error, so we bail out.
@@ -925,9 +926,10 @@
   return OK;
 }
 
-int HttpStreamParser::FindAndParseResponseHeaders() {
-  int end_offset = -1;
+int HttpStreamParser::FindAndParseResponseHeaders(int new_bytes) {
+  DCHECK_GT(new_bytes, 0);
   DCHECK_EQ(0, read_buf_unused_offset_);
+  int end_offset = -1;
 
   // Look for the start of the status line, if it hasn't been found yet.
   if (response_header_start_offset_ < 0) {
@@ -936,9 +938,16 @@
   }
 
   if (response_header_start_offset_ >= 0) {
-    end_offset = HttpUtil::LocateEndOfHeaders(read_buf_->StartOfBuffer(),
-                                              read_buf_->offset(),
-                                              response_header_start_offset_);
+    // LocateEndOfHeaders looks for two line breaks in a row (With or without
+    // carriage returns). So the end of the headers includes at most the last 3
+    // bytes of the buffer from the past read. This optimization avoids O(n^2)
+    // performance in the case each read only returns a couple bytes. It's not
+    // too important in production, but for fuzzers with memory instrumentation,
+    // it's needed to avoid timing out.
+    int search_start = std::max(response_header_start_offset_,
+                                read_buf_->offset() - new_bytes - 3);
+    end_offset = HttpUtil::LocateEndOfHeaders(
+        read_buf_->StartOfBuffer(), read_buf_->offset(), search_start);
   } else if (read_buf_->offset() >= 8) {
     // Enough data to decide that this is an HTTP/0.9 response.
     // 8 bytes = (4 bytes of junk) + "http".length()
diff --git a/net/http/http_stream_parser.h b/net/http/http_stream_parser.h
index 69c51f62..5763fd55 100644
--- a/net/http/http_stream_parser.h
+++ b/net/http/http_stream_parser.h
@@ -189,7 +189,11 @@
   // found, parse them with DoParseResponseHeaders().  Return the offset for
   // the end of the headers, or -1 if the complete headers were not found, or
   // with a net::Error if we encountered an error during parsing.
-  int FindAndParseResponseHeaders();
+  //
+  // |new_bytes| is the number of new bytes that have been appended to the end
+  // of |read_buf_| since the last call to this method (which must have returned
+  // -1).
+  int FindAndParseResponseHeaders(int new_bytes);
 
   // Parse the headers into response_.  Returns OK on success or a net::Error on
   // failure.
diff --git a/net/http/http_stream_parser_unittest.cc b/net/http/http_stream_parser_unittest.cc
index 7b6de3d..afe1e528 100644
--- a/net/http/http_stream_parser_unittest.cc
+++ b/net/http/http_stream_parser_unittest.cc
@@ -1224,14 +1224,11 @@
     read_buffer_->set_offset(offset + size);
   }
 
-  void AddRead(const std::string& data) {
-    reads_.push_back(MockRead(SYNCHRONOUS, sequence_number_++, data.data()));
-  }
-
-  // Simple overload - the above method requires using std::strings that outlive
-  // the function call.  This version works with inlined C-style strings.
-  void AddRead(const char* data) {
-    reads_.push_back(MockRead(SYNCHRONOUS, sequence_number_++, data));
+  // The data used to back |string_piece| must stay alive until all mock data
+  // has been read.
+  void AddRead(base::StringPiece string_piece) {
+    reads_.push_back(MockRead(SYNCHRONOUS, string_piece.data(),
+                              string_piece.length(), sequence_number_++));
   }
 
   void SetupParserAndSendRequest() {
@@ -1265,18 +1262,21 @@
 
   void ReadHeaders() { ReadHeadersExpectingError(OK); }
 
-  void ReadBody(int user_buf_len, int* read_lengths) {
+  std::string ReadBody(int user_buf_len, int* read_lengths) {
     TestCompletionCallback callback;
     scoped_refptr<IOBuffer> buffer = new IOBuffer(user_buf_len);
     int rv;
     int i = 0;
+    std::string body;
     while (true) {
       rv = parser_->ReadResponseBody(
           buffer.get(), user_buf_len, callback.callback());
       EXPECT_EQ(read_lengths[i], rv);
+      if (rv > 0)
+        body.append(buffer->data(), rv);
       i++;
       if (rv <= 0)
-        return;
+        return body;
     }
   }
 
@@ -1711,6 +1711,34 @@
   EXPECT_EQ(CountReadBytes(reads, arraysize(reads)), parser.received_bytes());
 }
 
+// Case where one byte is received at a time.
+TEST(HttpStreamParser, ReceiveOneByteAtATime) {
+  const std::string kResponseHeaders =
+      "HTTP/1.0 200 OK\r\n"
+      "Foo: Bar\r\n\r\n";
+  const std::string kResponseBody = "hi";
+
+  SimpleGetRunner get_runner;
+  for (size_t i = 0; i < kResponseHeaders.length(); ++i) {
+    get_runner.AddRead(base::StringPiece(kResponseHeaders.data() + i, 1));
+  }
+  for (size_t i = 0; i < kResponseBody.length(); ++i) {
+    get_runner.AddRead(base::StringPiece(kResponseBody.data() + i, 1));
+  }
+  // EOF
+  get_runner.AddRead("");
+
+  get_runner.SetupParserAndSendRequest();
+  get_runner.ReadHeaders();
+  std::string header_value;
+  EXPECT_TRUE(get_runner.response_info()->headers->GetNormalizedHeader(
+      "Foo", &header_value));
+  EXPECT_EQ("Bar", header_value);
+  int read_lengths[] = {1, 1, 0};
+  EXPECT_EQ(kResponseBody,
+            get_runner.ReadBody(kResponseBody.size(), read_lengths));
+}
+
 }  // namespace
 
 }  // namespace net
diff --git a/net/log/net_log_util.cc b/net/log/net_log_util.cc
index 6f5eb79..4cef960 100644
--- a/net/log/net_log_util.cc
+++ b/net/log/net_log_util.cc
@@ -320,10 +320,11 @@
         context->proxy_resolution_service();
 
     std::unique_ptr<base::DictionaryValue> dict(new base::DictionaryValue());
-    if (proxy_resolution_service->fetched_config().is_valid())
-      dict->Set("original", proxy_resolution_service->fetched_config().ToValue());
-    if (proxy_resolution_service->config().is_valid())
-      dict->Set("effective", proxy_resolution_service->config().ToValue());
+    if (proxy_resolution_service->fetched_config())
+      dict->Set("original",
+                proxy_resolution_service->fetched_config()->ToValue());
+    if (proxy_resolution_service->config())
+      dict->Set("effective", proxy_resolution_service->config()->ToValue());
 
     net_info_dict->Set(NetInfoSourceToString(NET_INFO_PROXY_SETTINGS),
                        std::move(dict));
diff --git a/net/nqe/external_estimate_provider.h b/net/nqe/external_estimate_provider.h
deleted file mode 100644
index 66c49b1..0000000
--- a/net/nqe/external_estimate_provider.h
+++ /dev/null
@@ -1,58 +0,0 @@
-// Copyright 2015 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef NET_NQE_EXTERNAL_ESTIMATE_PROVIDER_H_
-#define NET_NQE_EXTERNAL_ESTIMATE_PROVIDER_H_
-
-#include <stdint.h>
-
-#include "base/macros.h"
-#include "base/time/time.h"
-#include "net/base/net_export.h"
-
-namespace net {
-
-// Base class used by external providers such as operating system APIs to
-// provide network quality estimates to NetworkQualityEstimator.
-class NET_EXPORT ExternalEstimateProvider {
- public:
-  class NET_EXPORT UpdatedEstimateDelegate {
-   public:
-    // Will be called with updated RTT, and downstream throughput (in kilobits
-    // per second) when an updated estimate is available. If the estimate is
-    // unavailable, it is set to a negative value.
-    virtual void OnUpdatedEstimateAvailable(
-        const base::TimeDelta& rtt,
-        int32_t downstream_throughput_kbps) = 0;
-
-   protected:
-    UpdatedEstimateDelegate() {}
-    virtual ~UpdatedEstimateDelegate() {}
-
-   private:
-    DISALLOW_COPY_AND_ASSIGN(UpdatedEstimateDelegate);
-  };
-
-  ExternalEstimateProvider() {}
-  virtual ~ExternalEstimateProvider() {}
-
-  // Requests the provider to clear its cached network quality estimate.
-  virtual void ClearCachedEstimate() = 0;
-
-  // Sets delegate that is notified when an updated estimate is available.
-  // |delegate| should outlive |ExternalEstimateProvider|.
-  virtual void SetUpdatedEstimateDelegate(
-      UpdatedEstimateDelegate* delegate) = 0;
-
-  // Requests an updated network quality estimate from the external estimate
-  // provider.
-  virtual void Update() const = 0;
-
- private:
-  DISALLOW_COPY_AND_ASSIGN(ExternalEstimateProvider);
-};
-
-}  // namespace net
-
-#endif  // NET_NQE_EXTERNAL_ESTIMATE_PROVIDER_H_
diff --git a/net/nqe/network_quality_estimator.cc b/net/nqe/network_quality_estimator.cc
index 2767f32..45a1565b 100644
--- a/net/nqe/network_quality_estimator.cc
+++ b/net/nqe/network_quality_estimator.cc
@@ -161,7 +161,6 @@
 }  // namespace
 
 NetworkQualityEstimator::NetworkQualityEstimator(
-    std::unique_ptr<ExternalEstimateProvider> external_estimates_provider,
     std::unique_ptr<NetworkQualityEstimatorParams> params,
     NetLog* net_log)
     : params_(std::move(params)),
diff --git a/net/nqe/network_quality_estimator.h b/net/nqe/network_quality_estimator.h
index 732222cd..5c63cdf0 100644
--- a/net/nqe/network_quality_estimator.h
+++ b/net/nqe/network_quality_estimator.h
@@ -28,7 +28,6 @@
 #include "net/nqe/effective_connection_type.h"
 #include "net/nqe/effective_connection_type_observer.h"
 #include "net/nqe/event_creator.h"
-#include "net/nqe/external_estimate_provider.h"
 #include "net/nqe/network_id.h"
 #include "net/nqe/network_quality.h"
 #include "net/nqe/network_quality_estimator_params.h"
@@ -105,11 +104,10 @@
   };
 
   // Creates a new NetworkQualityEstimator.
-  // |external_estimates_provider| may be NULL. |params| contains the
+  // |params| contains the
   // configuration parameters relevant to network quality estimator. The caller
   // must guarantee that |net_log| outlives |this|.
   NetworkQualityEstimator(
-      std::unique_ptr<ExternalEstimateProvider> external_estimates_provider,
       std::unique_ptr<NetworkQualityEstimatorParams> params,
       NetLog* net_log);
 
diff --git a/net/nqe/network_quality_estimator_test_util.cc b/net/nqe/network_quality_estimator_test_util.cc
index 4ac729d..5a99a54f 100644
--- a/net/nqe/network_quality_estimator_test_util.cc
+++ b/net/nqe/network_quality_estimator_test_util.cc
@@ -9,7 +9,6 @@
 #include "net/base/load_flags.h"
 #include "net/log/net_log_with_source.h"
 #include "net/log/test_net_log_entry.h"
-#include "net/nqe/external_estimate_provider.h"
 #include "net/nqe/network_quality_estimator_params.h"
 #include "net/test/embedded_test_server/http_response.h"
 #include "net/traffic_annotation/network_traffic_annotation_test_helper.h"
@@ -31,40 +30,28 @@
 TestNetworkQualityEstimator::TestNetworkQualityEstimator(
     const std::map<std::string, std::string>& variation_params)
     : TestNetworkQualityEstimator(variation_params,
-                                  std::unique_ptr<ExternalEstimateProvider>()) {
-}
-
-TestNetworkQualityEstimator::TestNetworkQualityEstimator(
-    const std::map<std::string, std::string>& variation_params,
-    std::unique_ptr<net::ExternalEstimateProvider> external_estimate_provider)
-    : TestNetworkQualityEstimator(std::move(external_estimate_provider),
-                                  variation_params,
                                   true,
                                   true,
                                   std::make_unique<BoundTestNetLog>()) {}
 
 TestNetworkQualityEstimator::TestNetworkQualityEstimator(
-    std::unique_ptr<net::ExternalEstimateProvider> external_estimate_provider,
     const std::map<std::string, std::string>& variation_params,
     bool allow_local_host_requests_for_tests,
     bool allow_smaller_responses_for_tests,
     std::unique_ptr<BoundTestNetLog> net_log)
-    : TestNetworkQualityEstimator(std::move(external_estimate_provider),
-                                  variation_params,
+    : TestNetworkQualityEstimator(variation_params,
                                   allow_local_host_requests_for_tests,
                                   allow_smaller_responses_for_tests,
                                   false,
                                   std::move(net_log)) {}
 
 TestNetworkQualityEstimator::TestNetworkQualityEstimator(
-    std::unique_ptr<net::ExternalEstimateProvider> external_estimate_provider,
     const std::map<std::string, std::string>& variation_params,
     bool allow_local_host_requests_for_tests,
     bool allow_smaller_responses_for_tests,
     bool suppress_notifications_for_testing,
     std::unique_ptr<BoundTestNetLog> net_log)
     : NetworkQualityEstimator(
-          std::move(external_estimate_provider),
           std::make_unique<NetworkQualityEstimatorParams>(variation_params),
           net_log->bound().net_log()),
       net_log_(std::move(net_log)),
@@ -87,9 +74,7 @@
 TestNetworkQualityEstimator::TestNetworkQualityEstimator(
     std::unique_ptr<NetworkQualityEstimatorParams> params,
     std::unique_ptr<BoundTestNetLog> net_log)
-    : NetworkQualityEstimator(std::unique_ptr<ExternalEstimateProvider>(),
-                              std::move(params),
-                              net_log->bound().net_log()),
+    : NetworkQualityEstimator(std::move(params), net_log->bound().net_log()),
       net_log_(std::move(net_log)),
       current_network_type_(NetworkChangeNotifier::CONNECTION_UNKNOWN),
       accuracy_recording_intervals_set_(false),
diff --git a/net/nqe/network_quality_estimator_test_util.h b/net/nqe/network_quality_estimator_test_util.h
index 3b18867..868148b 100644
--- a/net/nqe/network_quality_estimator_test_util.h
+++ b/net/nqe/network_quality_estimator_test_util.h
@@ -28,8 +28,6 @@
 
 namespace net {
 
-class ExternalEstimateProvider;
-
 // Helps in setting the current network type and id.
 class TestNetworkQualityEstimator : public NetworkQualityEstimator {
  public:
@@ -40,18 +38,11 @@
 
   TestNetworkQualityEstimator(
       const std::map<std::string, std::string>& variation_params,
-      std::unique_ptr<net::ExternalEstimateProvider>
-          external_estimate_provider);
-
-  TestNetworkQualityEstimator(
-      std::unique_ptr<net::ExternalEstimateProvider> external_estimate_provider,
-      const std::map<std::string, std::string>& variation_params,
       bool allow_local_host_requests_for_tests,
       bool allow_smaller_responses_for_tests,
       std::unique_ptr<BoundTestNetLog> net_log);
 
   TestNetworkQualityEstimator(
-      std::unique_ptr<net::ExternalEstimateProvider> external_estimate_provider,
       const std::map<std::string, std::string>& variation_params,
       bool allow_local_host_requests_for_tests,
       bool allow_smaller_responses_for_tests,
diff --git a/net/nqe/network_quality_estimator_unittest.cc b/net/nqe/network_quality_estimator_unittest.cc
index 9ece460f7..fab3567 100644
--- a/net/nqe/network_quality_estimator_unittest.cc
+++ b/net/nqe/network_quality_estimator_unittest.cc
@@ -33,7 +33,6 @@
 #include "net/log/test_net_log.h"
 #include "net/nqe/effective_connection_type.h"
 #include "net/nqe/effective_connection_type_observer.h"
-#include "net/nqe/external_estimate_provider.h"
 #include "net/nqe/network_quality_estimator_test_util.h"
 #include "net/nqe/network_quality_observation.h"
 #include "net/nqe/network_quality_observation_source.h"
@@ -662,9 +661,8 @@
   TestRTTObserver rtt_observer;
   TestThroughputObserver throughput_observer;
   std::map<std::string, std::string> variation_params;
-  TestNetworkQualityEstimator estimator(
-      nullptr, variation_params, false, false,
-      std::make_unique<BoundTestNetLog>());
+  TestNetworkQualityEstimator estimator(variation_params, false, false,
+                                        std::make_unique<BoundTestNetLog>());
 
   // Default observations should be added when constructing the |estimator|.
   histogram_tester.ExpectBucketCount(
@@ -800,9 +798,8 @@
   // Negative variation value should not be used.
   variation_params["2G.DefaultMedianTransportRTTMsec"] = "-5";
 
-  TestNetworkQualityEstimator estimator(
-      nullptr, variation_params, false, false,
-      std::make_unique<BoundTestNetLog>());
+  TestNetworkQualityEstimator estimator(variation_params, false, false,
+                                        std::make_unique<BoundTestNetLog>());
   estimator.SimulateNetworkChange(
       NetworkChangeNotifier::ConnectionType::CONNECTION_UNKNOWN, "unknown-1");
 
@@ -1362,11 +1359,10 @@
   };
 
   for (const auto& test : tests) {
-    TestNetworkQualityEstimator estimator(
-        std::unique_ptr<net::ExternalEstimateProvider>(), variation_params,
-        test.allow_small_localhost_requests,
-        test.allow_small_localhost_requests,
-        std::make_unique<BoundTestNetLog>());
+    TestNetworkQualityEstimator estimator(variation_params,
+                                          test.allow_small_localhost_requests,
+                                          test.allow_small_localhost_requests,
+                                          std::make_unique<BoundTestNetLog>());
 
     base::TimeDelta rtt;
     EXPECT_FALSE(estimator.GetRecentHttpRTT(base::TimeTicks(), &rtt));
@@ -2045,9 +2041,8 @@
   std::map<std::string, std::string> variation_params;
   variation_params["persistent_cache_reading_enabled"] = "true";
   variation_params["throughput_min_requests_in_flight"] = "1";
-  TestNetworkQualityEstimator estimator(
-      nullptr, variation_params, true, true,
-      std::make_unique<BoundTestNetLog>());
+  TestNetworkQualityEstimator estimator(variation_params, true, true,
+                                        std::make_unique<BoundTestNetLog>());
   estimator.SetTickClockForTesting(&tick_clock);
   estimator.SimulateNetworkChange(
       NetworkChangeNotifier::ConnectionType::CONNECTION_2G, "test");
@@ -2595,9 +2590,8 @@
   variation_params["add_default_platform_observations"] = "false";
   // Disable default platform values so that the effect of cached estimates
   // at the time of startup can be studied in isolation.
-  TestNetworkQualityEstimator estimator(
-      std::unique_ptr<net::ExternalEstimateProvider>(), variation_params, true,
-      true, std::make_unique<BoundTestNetLog>());
+  TestNetworkQualityEstimator estimator(variation_params, true, true,
+                                        std::make_unique<BoundTestNetLog>());
 
   // Add observers.
   TestRTTObserver rtt_observer;
@@ -2709,9 +2703,8 @@
 
   // Disable default platform values so that the effect of cached estimates
   // at the time of startup can be studied in isolation.
-  TestNetworkQualityEstimator estimator(
-      std::unique_ptr<net::ExternalEstimateProvider>(), variation_params, true,
-      true, std::make_unique<BoundTestNetLog>());
+  TestNetworkQualityEstimator estimator(variation_params, true, true,
+                                        std::make_unique<BoundTestNetLog>());
 
   // Add observers.
   TestRTTObserver rtt_observer;
@@ -2916,9 +2909,8 @@
   variation_params["add_default_platform_observations"] = "false";
   // Disable default platform values so that the effect of cached estimates
   // at the time of startup can be studied in isolation.
-  TestNetworkQualityEstimator estimator(
-      std::unique_ptr<net::ExternalEstimateProvider>(), variation_params, true,
-      true, std::make_unique<BoundTestNetLog>());
+  TestNetworkQualityEstimator estimator(variation_params, true, true,
+                                        std::make_unique<BoundTestNetLog>());
 
   // Add observers.
   TestRTTObserver rtt_observer;
diff --git a/net/proxy_resolution/proxy_config.cc b/net/proxy_resolution/proxy_config.cc
index e4180fe9..f33bab6 100644
--- a/net/proxy_resolution/proxy_config.cc
+++ b/net/proxy_resolution/proxy_config.cc
@@ -191,9 +191,9 @@
 }
 
 ProxyConfig::ProxyConfig()
-    : auto_detect_(false), pac_mandatory_(false),
-      source_(PROXY_CONFIG_SOURCE_UNKNOWN), id_(kInvalidConfigID)  {
-}
+    : auto_detect_(false),
+      pac_mandatory_(false),
+      source_(PROXY_CONFIG_SOURCE_UNKNOWN) {}
 
 ProxyConfig::ProxyConfig(const ProxyConfig& config) = default;
 
diff --git a/net/proxy_resolution/proxy_config.h b/net/proxy_resolution/proxy_config.h
index cd68226..0ae0317 100644
--- a/net/proxy_resolution/proxy_config.h
+++ b/net/proxy_resolution/proxy_config.h
@@ -148,23 +148,13 @@
     const ProxyList* GetProxyListForWebSocketScheme() const;
   };
 
-  typedef int ID;
-
-  // Indicates an invalid proxy config.
-  static const ID kInvalidConfigID = 0;
-
   ProxyConfig();
   ProxyConfig(const ProxyConfig& config);
   ~ProxyConfig();
   ProxyConfig& operator=(const ProxyConfig& config);
 
-  // Used to numerically identify this configuration.
-  ID id() const { return id_; }
-  void set_id(ID id) { id_ = id; }
-  bool is_valid() const { return id_ != kInvalidConfigID; }
-
   // Returns true if the given config is equivalent to this config.  The
-  // comparison ignores differences in |id()| and |source()|.
+  // comparison ignores differences in |source()|.
   bool Equals(const ProxyConfig& other) const;
 
   // Returns true if this config contains any "automatic" settings. See the
@@ -256,8 +246,6 @@
 
   // Source of proxy settings.
   ProxyConfigSource source_;
-
-  ID id_;
 };
 
 }  // namespace net
diff --git a/net/proxy_resolution/proxy_config_service_linux.cc b/net/proxy_resolution/proxy_config_service_linux.cc
index 2f953fd..9f2db1c 100644
--- a/net/proxy_resolution/proxy_config_service_linux.cc
+++ b/net/proxy_resolution/proxy_config_service_linux.cc
@@ -82,6 +82,16 @@
   return host;
 }
 
+ProxyConfig GetConfigOrDirect(
+    const base::Optional<ProxyConfig>& optional_config) {
+  if (optional_config)
+    return optional_config.value();
+
+  ProxyConfig config = ProxyConfig::CreateDirect();
+  config.set_source(PROXY_CONFIG_SOURCE_SYSTEM_FAILED);
+  return config;
+}
+
 }  // namespace
 
 ProxyConfigServiceLinux::Delegate::~Delegate() = default;
@@ -115,7 +125,11 @@
                                      result_server);
 }
 
-bool ProxyConfigServiceLinux::Delegate::GetConfigFromEnv(ProxyConfig* config) {
+base::Optional<ProxyConfig>
+ProxyConfigServiceLinux::Delegate::GetConfigFromEnv() {
+  base::Optional<ProxyConfig> config;
+  config.emplace();
+
   // Check for automatic configuration first, in
   // "auto_proxy". Possibly only the "environment_proxy" firefox
   // extension has ever used this, but it still sounds like a good
@@ -129,7 +143,7 @@
       // specified autoconfig URL
       config->set_pac_url(GURL(auto_proxy));
     }
-    return true;
+    return config;
   }
   // "all_proxy" is a shortcut to avoid defining {http,https,ftp}_proxy.
   ProxyServer proxy_server;
@@ -180,13 +194,13 @@
     // explicit that env vars do specify a configuration: having no
     // rules specified only means the user explicitly asks for direct
     // connections.
-    return !no_proxy.empty();
+    return !no_proxy.empty() ? config : base::Optional<ProxyConfig>();
   }
   // Note that this uses "suffix" matching. So a bypass of "google.com"
   // is understood to mean a bypass of "*google.com".
   config->proxy_rules().bypass_rules.ParseFromStringUsingSuffixMatching(
       no_proxy);
-  return true;
+  return config;
 }
 
 namespace {
@@ -1012,17 +1026,20 @@
   return false;
 }
 
-bool ProxyConfigServiceLinux::Delegate::GetConfigFromSettings(
-    ProxyConfig* config) {
+base::Optional<ProxyConfig>
+ProxyConfigServiceLinux::Delegate::GetConfigFromSettings() {
+  base::Optional<ProxyConfig> config;
+  config.emplace();
+
   std::string mode;
   if (!setting_getter_->GetString(SettingGetter::PROXY_MODE, &mode)) {
     // We expect this to always be set, so if we don't see it then we probably
     // have a gsettings problem, and so we don't have a valid proxy config.
-    return false;
+    return base::Optional<ProxyConfig>();
   }
   if (mode == "none") {
     // Specifically specifies no proxy.
-    return true;
+    return config;
   }
 
   if (mode == "auto") {
@@ -1036,18 +1053,18 @@
           pac_url_str = "file://" + pac_url_str;
         GURL pac_url(pac_url_str);
         if (!pac_url.is_valid())
-          return false;
+          return base::Optional<ProxyConfig>();
         config->set_pac_url(pac_url);
-        return true;
+        return config;
       }
     }
     config->set_auto_detect(true);
-    return true;
+    return config;
   }
 
   if (mode != "manual") {
     // Mode is unrecognized.
-    return false;
+    return base::Optional<ProxyConfig>();
   }
   bool use_http_proxy;
   if (setting_getter_->GetBool(SettingGetter::PROXY_USE_HTTP_PROXY,
@@ -1055,7 +1072,7 @@
       && !use_http_proxy) {
     // Another master switch for some reason. If set to false, then no
     // proxy. But we don't panic if the key doesn't exist.
-    return true;
+    return config;
   }
 
   bool same_proxy = false;
@@ -1110,7 +1127,7 @@
 
   if (config->proxy_rules().empty()) {
     // Manual mode but we couldn't parse any rules.
-    return false;
+    return base::Optional<ProxyConfig>();
   }
 
   // Check for authentication, just so we can warn.
@@ -1147,7 +1164,7 @@
   config->proxy_rules().reverse_bypass =
       setting_getter_->BypassListIsReversed();
 
-  return true;
+  return config;
 }
 
 ProxyConfigServiceLinux::Delegate::Delegate(
@@ -1214,24 +1231,23 @@
   // does so even if the proxy mode is set to auto, which would
   // mislead us.
 
-  bool got_config = false;
-  if (setting_getter_ && setting_getter_->Init(glib_task_runner) &&
-      GetConfigFromSettings(&cached_config_)) {
-    cached_config_.set_id(1);  // Mark it as valid.
-    cached_config_.set_source(setting_getter_->GetConfigSource());
+  cached_config_ = base::Optional<ProxyConfig>();
+  if (setting_getter_ && setting_getter_->Init(glib_task_runner)) {
+    cached_config_ = GetConfigFromSettings();
+  }
+  if (cached_config_) {
+    cached_config_->set_source(setting_getter_->GetConfigSource());
     VLOG(1) << "Obtained proxy settings from "
-            << ProxyConfigSourceToString(cached_config_.source());
+            << ProxyConfigSourceToString(cached_config_->source());
 
     // If gsettings proxy mode is "none", meaning direct, then we take
     // that to be a valid config and will not check environment
     // variables. The alternative would have been to look for a proxy
-    // whereever we can find one.
-    got_config = true;
+    // wherever we can find one.
 
     // Keep a copy of the config for use from this thread for
     // comparison with updated settings when we get notifications.
     reference_config_ = cached_config_;
-    reference_config_.set_id(1);  // Mark it as valid.
 
     // We only set up notifications if we have the main and file loops
     // available. We do this after getting the initial configuration so that we
@@ -1255,14 +1271,14 @@
     }
   }
 
-  if (!got_config) {
+  if (!cached_config_) {
     // We fall back on environment variables.
     //
     // Consulting environment variables doesn't need to be done from the
     // default glib main loop, but it's a tiny enough amount of work.
-    if (GetConfigFromEnv(&cached_config_)) {
-      cached_config_.set_source(PROXY_CONFIG_SOURCE_ENV);
-      cached_config_.set_id(1);  // Mark it as valid.
+    cached_config_ = GetConfigFromEnv();
+    if (cached_config_) {
+      cached_config_->set_source(PROXY_CONFIG_SOURCE_ENV);
       VLOG(1) << "Obtained proxy settings from environment variables";
     }
   }
@@ -1295,12 +1311,7 @@
 
   // Simply return the last proxy configuration that glib_default_loop
   // notified us of.
-  if (cached_config_.is_valid()) {
-    *config = cached_config_;
-  } else {
-    *config = ProxyConfig::CreateDirect();
-    config->set_source(PROXY_CONFIG_SOURCE_SYSTEM_FAILED);
-  }
+  *config = GetConfigOrDirect(cached_config_);
 
   // We return CONFIG_VALID to indicate that *config was filled in. It is always
   // going to be available since we initialized eagerly on the UI thread.
@@ -1316,14 +1327,11 @@
   scoped_refptr<base::SequencedTaskRunner> required_loop =
       setting_getter_->GetNotificationTaskRunner();
   DCHECK(!required_loop.get() || required_loop->RunsTasksInCurrentSequence());
-  ProxyConfig new_config;
-  bool valid = GetConfigFromSettings(&new_config);
-  if (valid)
-    new_config.set_id(1);  // mark it as valid
+  base::Optional<ProxyConfig> new_config = GetConfigFromSettings();
 
   // See if it is different from what we had before.
-  if (new_config.is_valid() != reference_config_.is_valid() ||
-      !new_config.Equals(reference_config_)) {
+  if (new_config.has_value() != reference_config_.has_value() ||
+      !new_config->Equals(*reference_config_)) {
     // Post a task to the main TaskRunner with the new configuration, so it can
     // update |cached_config_|.
     main_task_runner_->PostTask(
@@ -1338,12 +1346,14 @@
 }
 
 void ProxyConfigServiceLinux::Delegate::SetNewProxyConfig(
-    const ProxyConfig& new_config) {
+    const base::Optional<ProxyConfig>& new_config) {
   DCHECK(main_task_runner_->RunsTasksInCurrentSequence());
   VLOG(1) << "Proxy configuration changed";
   cached_config_ = new_config;
-  for (auto& observer : observers_)
-    observer.OnProxyConfigChanged(new_config, ProxyConfigService::CONFIG_VALID);
+  for (auto& observer : observers_) {
+    observer.OnProxyConfigChanged(GetConfigOrDirect(new_config),
+                                  ProxyConfigService::CONFIG_VALID);
+  }
 }
 
 void ProxyConfigServiceLinux::Delegate::PostDestroyTask() {
diff --git a/net/proxy_resolution/proxy_config_service_linux.h b/net/proxy_resolution/proxy_config_service_linux.h
index 58c1508..1debdc1 100644
--- a/net/proxy_resolution/proxy_config_service_linux.h
+++ b/net/proxy_resolution/proxy_config_service_linux.h
@@ -16,6 +16,7 @@
 #include "base/macros.h"
 #include "base/memory/ref_counted.h"
 #include "base/observer_list.h"
+#include "base/optional.h"
 #include "net/base/net_export.h"
 #include "net/base/proxy_server.h"
 #include "net/proxy_resolution/proxy_config.h"
@@ -222,22 +223,22 @@
     // As above but with scheme set to HTTP, for convenience.
     bool GetProxyFromEnvVar(base::StringPiece variable,
                             ProxyServer* result_server);
-    // Fills proxy config from environment variables. Returns true if
-    // variables were found and the configuration is valid.
-    bool GetConfigFromEnv(ProxyConfig* config);
+    // Returns a proxy config based on the environment variables, or empty value
+    // on failure.
+    base::Optional<ProxyConfig> GetConfigFromEnv();
 
     // Obtains host and port config settings and parses a proxy server
     // specification from it and puts it in result. Returns true if the
     // requested variable is defined and the value valid.
     bool GetProxyFromSettings(SettingGetter::StringSetting host_key,
                               ProxyServer* result_server);
-    // Fills proxy config from settings. Returns true if settings were found
-    // and the configuration is valid.
-    bool GetConfigFromSettings(ProxyConfig* config);
+    // Returns a proxy config based on the settings, or empty value
+    // on failure.
+    base::Optional<ProxyConfig> GetConfigFromSettings();
 
     // This method is posted from the glib thread to the main TaskRunner to
     // carry the new config information.
-    void SetNewProxyConfig(const ProxyConfig& new_config);
+    void SetNewProxyConfig(const base::Optional<ProxyConfig>& new_config);
 
     // This method is run on the getter's notification thread.
     void SetUpNotifications();
@@ -248,12 +249,12 @@
     // Cached proxy configuration, to be returned by
     // GetLatestProxyConfig. Initially populated from the glib thread, but
     // afterwards only accessed from the main TaskRunner.
-    ProxyConfig cached_config_;
+    base::Optional<ProxyConfig> cached_config_;
 
     // A copy kept on the glib thread of the last seen proxy config, so as
     // to avoid posting a call to SetNewProxyConfig when we get a
     // notification but the config has not actually changed.
-    ProxyConfig reference_config_;
+    base::Optional<ProxyConfig> reference_config_;
 
     // The task runner for the glib thread, aka main browser thread. This thread
     // is where we run the glib main loop (see
diff --git a/net/proxy_resolution/proxy_info.cc b/net/proxy_resolution/proxy_info.cc
index 5879668..dbcc8c4 100644
--- a/net/proxy_resolution/proxy_info.cc
+++ b/net/proxy_resolution/proxy_info.cc
@@ -9,11 +9,9 @@
 namespace net {
 
 ProxyInfo::ProxyInfo()
-    : config_id_(ProxyConfig::kInvalidConfigID),
-      config_source_(PROXY_CONFIG_SOURCE_UNKNOWN),
+    : config_source_(PROXY_CONFIG_SOURCE_UNKNOWN),
       did_bypass_proxy_(false),
-      did_use_pac_script_(false) {
-}
+      did_use_pac_script_(false) {}
 
 ProxyInfo::ProxyInfo(const ProxyInfo& other) = default;
 
@@ -24,7 +22,6 @@
   proxy_resolve_end_time_ = other.proxy_resolve_end_time_;
   proxy_list_ = other.proxy_list_;
   proxy_retry_info_ = other.proxy_retry_info_;
-  config_id_ = other.config_id_;
   config_source_ = other.config_source_;
   did_bypass_proxy_ = other.did_bypass_proxy_;
   did_use_pac_script_ = other.did_use_pac_script_;
@@ -91,7 +88,6 @@
   proxy_list_.Clear();
   alternative_proxy_ = net::ProxyServer();
   proxy_retry_info_.clear();
-  config_id_ = ProxyConfig::kInvalidConfigID;
   config_source_ = PROXY_CONFIG_SOURCE_UNKNOWN;
   did_bypass_proxy_ = false;
   did_use_pac_script_ = false;
diff --git a/net/proxy_resolution/proxy_info.h b/net/proxy_resolution/proxy_info.h
index d052c32..bf65e4a3 100644
--- a/net/proxy_resolution/proxy_info.h
+++ b/net/proxy_resolution/proxy_info.h
@@ -32,11 +32,6 @@
   void Use(const ProxyInfo& proxy_info);
 
   // Uses a direct connection.
-  //
-  // Note that this method resets this instance unlike Fallback(), etc. which
-  // only modify |proxy_list_|. For example, since |config_id_| is cleared, the
-  // ProxyResolutionService may recognize this instance as a new config after
-  // UseDirect() call.
   void UseDirect();
 
   // Uses a direct connection. did_bypass_proxy() will return true to indicate
@@ -148,8 +143,8 @@
   // Marks the current proxy as bad. |net_error| should contain the network
   // error encountered when this proxy was tried, if any. If this fallback
   // is not because of a network error, then |OK| should be passed in (eg. for
-  // reasons such as local policy). Returns true if there is another proxy is
-  // available to try in proxy list_.
+  // reasons such as local policy). Returns true if there is another proxy
+  // available to try in |proxy_list_|.
   bool Fallback(int net_error, const NetLogWithSource& net_log);
 
   // De-prioritizes the proxies that we have cached as not working, by moving
@@ -159,8 +154,6 @@
   // Deletes any entry which doesn't have one of the specified proxy schemes.
   void RemoveProxiesWithoutScheme(int scheme_bit_field);
 
-  ProxyConfig::ID config_id() const { return config_id_; }
-
   // Returns the list of proxies to use.
   const ProxyList& proxy_list() const {
     return proxy_list_;
@@ -199,9 +192,6 @@
   // List of proxies that have been tried already.
   ProxyRetryInfoMap proxy_retry_info_;
 
-  // This value identifies the proxy config used to initialize this object.
-  ProxyConfig::ID config_id_;
-
   // The source of the proxy settings used,
   ProxyConfigSource config_source_;
 
diff --git a/net/proxy_resolution/proxy_info_unittest.cc b/net/proxy_resolution/proxy_info_unittest.cc
index 8aa3d67..8492c00a 100644
--- a/net/proxy_resolution/proxy_info_unittest.cc
+++ b/net/proxy_resolution/proxy_info_unittest.cc
@@ -48,15 +48,12 @@
 
 TEST(ProxyInfoTest, UseVsOverrideProxyList) {
   ProxyInfo info;
-  info.config_id_ = 99;
   ProxyList proxy_list;
   proxy_list.Set("http://foo.com");
   info.OverrideProxyList(proxy_list);
-  EXPECT_EQ(99, info.config_id_);
   EXPECT_EQ("PROXY foo.com:80", info.proxy_list().ToPacString());
   proxy_list.Set("http://bar.com");
   info.UseProxyList(proxy_list);
-  EXPECT_EQ(0, info.config_id_);
   EXPECT_EQ("PROXY bar.com:80", info.proxy_list().ToPacString());
 }
 
diff --git a/net/proxy_resolution/proxy_service.cc b/net/proxy_resolution/proxy_service.cc
index 62fbac0..6b63306 100644
--- a/net/proxy_resolution/proxy_service.cc
+++ b/net/proxy_resolution/proxy_service.cc
@@ -285,14 +285,14 @@
 
 // Returns NetLog parameters describing a proxy configuration change.
 std::unique_ptr<base::Value> NetLogProxyConfigChangedCallback(
-    const ProxyConfig* old_config,
+    const base::Optional<ProxyConfig>* old_config,
     const ProxyConfig* new_config,
     NetLogCaptureMode /* capture_mode */) {
   std::unique_ptr<base::DictionaryValue> dict(new base::DictionaryValue());
   // The "old_config" is optional -- the first notification will not have
   // any "previous" configuration.
-  if (old_config->is_valid())
-    dict->Set("old_config", old_config->ToValue());
+  if (old_config->has_value())
+    dict->Set("old_config", (*old_config)->ToValue());
   dict->Set("new_config", new_config->ToValue());
   return std::move(dict);
 }
@@ -795,7 +795,6 @@
         method_(method),
         proxy_delegate_(proxy_delegate),
         resolve_job_(nullptr),
-        config_id_(ProxyConfig::kInvalidConfigID),
         config_source_(PROXY_CONFIG_SOURCE_UNKNOWN),
         net_log_(net_log),
         creation_time_(TimeTicks::Now()) {
@@ -807,10 +806,8 @@
     DCHECK(!was_cancelled());
     DCHECK(!is_started());
 
-    DCHECK(service_->config_.is_valid());
-
-    config_id_ = service_->config_.id();
-    config_source_ = service_->config_.source();
+    DCHECK(service_->config_);
+    config_source_ = service_->config_->source();
 
     return resolver()->GetProxyForURL(
         url_, results_,
@@ -873,14 +870,12 @@
 
     // Make a note in the results which configuration was in use at the
     // time of the resolve.
-    results_->config_id_ = config_id_;
     results_->config_source_ = config_source_;
     results_->did_use_pac_script_ = true;
     results_->proxy_resolve_start_time_ = creation_time_;
     results_->proxy_resolve_end_time_ = TimeTicks::Now();
 
     // Reset the state associated with in-progress-resolve.
-    config_id_ = ProxyConfig::kInvalidConfigID;
     config_source_ = PROXY_CONFIG_SOURCE_UNKNOWN;
 
     return rv;
@@ -924,7 +919,6 @@
   std::string method_;
   ProxyDelegate* proxy_delegate_;
   std::unique_ptr<ProxyResolver::Request> resolve_job_;
-  ProxyConfig::ID config_id_;  // The config id when the resolve was started.
   ProxyConfigSource config_source_;  // The source of proxy settings.
   NetLogWithSource net_log_;
   // Time when the request was created.  Stored here rather than in |results_|
@@ -939,7 +933,6 @@
     std::unique_ptr<ProxyResolverFactory> resolver_factory,
     NetLog* net_log)
     : resolver_factory_(std::move(resolver_factory)),
-      next_config_id_(1),
       current_state_(STATE_NONE),
       net_log_(net_log),
       stall_proxy_auto_config_delay_(
@@ -1119,20 +1112,18 @@
   if (current_state_ != STATE_READY)
     return ERR_IO_PENDING;  // Still initializing.
 
-  DCHECK_NE(config_.id(), ProxyConfig::kInvalidConfigID);
-
+  DCHECK(config_);
   // If it was impossible to fetch or parse the PAC script, we cannot complete
   // the request here and bail out.
   if (permanent_error_ != OK)
     return permanent_error_;
 
-  if (config_.HasAutomaticSettings())
+  if (config_->HasAutomaticSettings())
     return ERR_IO_PENDING;  // Must submit the request to the proxy resolver.
 
   // Use the manual proxy settings.
-  config_.proxy_rules().Apply(url, result);
-  result->config_source_ = config_.source();
-  result->config_id_ = config_.id();
+  config_->proxy_rules().Apply(url, result);
+  result->config_source_ = config_->source();
 
   return OK;
 }
@@ -1195,7 +1186,7 @@
   config_service_->OnLazyPoll();
 
   // If we have already fetched the configuration, start applying it.
-  if (fetched_config_.is_valid()) {
+  if (fetched_config_) {
     InitializeUsingLastFetchedConfig();
     return;
   }
@@ -1216,7 +1207,8 @@
 void ProxyResolutionService::OnInitProxyResolverComplete(int result) {
   DCHECK_EQ(STATE_WAITING_FOR_INIT_PROXY_RESOLVER, current_state_);
   DCHECK(init_proxy_resolver_.get());
-  DCHECK(fetched_config_.HasAutomaticSettings());
+  DCHECK(fetched_config_);
+  DCHECK(fetched_config_->HasAutomaticSettings());
   config_ = init_proxy_resolver_->effective_config();
 
   // At this point we have decided which proxy settings to use (i.e. which PAC
@@ -1227,7 +1219,7 @@
   script_poller_.reset(new ProxyScriptDeciderPoller(
       base::Bind(&ProxyResolutionService::InitializeUsingDecidedConfig,
                  base::Unretained(this)),
-      fetched_config_, resolver_factory_->expects_pac_bytes(),
+      fetched_config_.value(), resolver_factory_->expects_pac_bytes(),
       proxy_script_fetcher_.get(), dhcp_proxy_script_fetcher_.get(), result,
       init_proxy_resolver_->script_data(), NULL));
   script_poller_->set_quick_check_enabled(quick_check_enabled_);
@@ -1235,7 +1227,7 @@
   init_proxy_resolver_.reset();
 
   if (result != OK) {
-    if (fetched_config_.pac_mandatory()) {
+    if (fetched_config_->pac_mandatory()) {
       VLOG(1) << "Failed configuring with mandatory PAC script, blocking all "
                  "traffic.";
       config_ = fetched_config_;
@@ -1244,60 +1236,19 @@
       VLOG(1) << "Failed configuring with PAC script, falling-back to manual "
                  "proxy servers.";
       config_ = fetched_config_;
-      config_.ClearAutomaticSettings();
+      config_->ClearAutomaticSettings();
       result = OK;
     }
   }
   permanent_error_ = result;
 
-  // TODO(eroman): Make this ID unique in the case where configuration changed
-  //               due to ProxyScriptDeciderPoller.
-  config_.set_id(fetched_config_.id());
-  config_.set_source(fetched_config_.source());
+  config_->set_source(fetched_config_->source());
 
   // Resume any requests which we had to defer until the PAC script was
   // downloaded.
   SetReady();
 }
 
-int ProxyResolutionService::ReconsiderProxyAfterError(
-    const GURL& url,
-    const std::string& method,
-    int net_error,
-    ProxyInfo* result,
-    const CompletionCallback& callback,
-    Request** pac_request,
-    ProxyDelegate* proxy_delegate,
-    const NetLogWithSource& net_log) {
-  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
-
-  // Check to see if we have a new config since ResolveProxy was called.  We
-  // want to re-run ResolveProxy in two cases: 1) we have a new config, or 2) a
-  // direct connection failed and we never tried the current config.
-
-  DCHECK(result);
-  bool re_resolve = result->config_id_ != config_.id();
-
-  if (re_resolve) {
-    // If we have a new config or the config was never tried, we delete the
-    // list of bad proxies and we try again.
-    proxy_retry_info_.clear();
-    return ResolveProxy(url, method, result, callback, pac_request,
-                        proxy_delegate, net_log);
-  }
-
-  DCHECK(!result->is_empty());
-  ProxyServer bad_proxy = result->proxy_server();
-
-  // We don't have new proxy settings to try, try to fallback to the next proxy
-  // in the list.
-  bool did_fallback = result->Fallback(net_error, net_log);
-
-  // Return synchronous failure if there is nothing left to fall-back to.
-  // TODO(eroman): This is a yucky API, clean it up.
-  return did_fallback ? OK : ERR_FAILED;
-}
-
 bool ProxyResolutionService::MarkProxiesAsBadUntil(
     const ProxyInfo& result,
     base::TimeDelta retry_delay,
@@ -1392,7 +1343,7 @@
         NetLogEventType::PROXY_SERVICE_RESOLVED_PROXY_LIST, result_code);
 
     bool reset_config = result_code == ERR_PAC_SCRIPT_TERMINATED;
-    if (!config_.pac_mandatory()) {
+    if (!config_->pac_mandatory()) {
       // Fall-back to direct when the proxy resolver fails. This corresponds
       // with a javascript runtime error in the PAC script.
       //
@@ -1462,9 +1413,9 @@
   init_proxy_resolver_.reset();
   SuspendAllPendingRequests();
   resolver_.reset();
-  config_ = ProxyConfig();
+  config_ = base::Optional<ProxyConfig>();
   if (reset_fetched_config)
-    fetched_config_ = ProxyConfig();
+    fetched_config_ = base::Optional<ProxyConfig>();
   current_state_ = STATE_NONE;
 
   return previous_state;
@@ -1578,7 +1529,6 @@
 
   // Set the new configuration as the most recently fetched one.
   fetched_config_ = effective_config;
-  fetched_config_.set_id(1);  // Needed for a later DCHECK of is_valid().
 
   InitializeUsingLastFetchedConfig();
 }
@@ -1586,12 +1536,8 @@
 void ProxyResolutionService::InitializeUsingLastFetchedConfig() {
   ResetProxyConfig(false);
 
-  DCHECK(fetched_config_.is_valid());
-
-  // Increment the ID to reflect that the config has changed.
-  fetched_config_.set_id(next_config_id_++);
-
-  if (!fetched_config_.HasAutomaticSettings()) {
+  DCHECK(fetched_config_);
+  if (!fetched_config_->HasAutomaticSettings()) {
     config_ = fetched_config_;
     SetReady();
     return;
@@ -1608,7 +1554,8 @@
   init_proxy_resolver_->set_quick_check_enabled(quick_check_enabled_);
   int rv = init_proxy_resolver_->Start(
       &resolver_, resolver_factory_.get(), proxy_script_fetcher_.get(),
-      dhcp_proxy_script_fetcher_.get(), net_log_, fetched_config_, wait_delay,
+      dhcp_proxy_script_fetcher_.get(), net_log_, fetched_config_.value(),
+      wait_delay,
       base::Bind(&ProxyResolutionService::OnInitProxyResolverComplete,
                  base::Unretained(this)));
 
@@ -1620,8 +1567,8 @@
     int decider_result,
     ProxyResolverScriptData* script_data,
     const ProxyConfig& effective_config) {
-  DCHECK(fetched_config_.is_valid());
-  DCHECK(fetched_config_.HasAutomaticSettings());
+  DCHECK(fetched_config_);
+  DCHECK(fetched_config_->HasAutomaticSettings());
 
   ResetProxyConfig(false);
 
diff --git a/net/proxy_resolution/proxy_service.h b/net/proxy_resolution/proxy_service.h
index 0609f835..9a7b1c4 100644
--- a/net/proxy_resolution/proxy_service.h
+++ b/net/proxy_resolution/proxy_service.h
@@ -15,6 +15,7 @@
 #include "base/gtest_prod_util.h"
 #include "base/macros.h"
 #include "base/memory/ref_counted.h"
+#include "base/optional.h"
 #include "base/synchronization/waitable_event.h"
 #include "base/threading/thread_checker.h"
 #include "net/base/completion_callback.h"
@@ -162,29 +163,6 @@
                                     ProxyDelegate* proxy_delegate,
                                     const NetLogWithSource& net_log);
 
-  // This method is called after a failure to connect or resolve a host name.
-  // It gives the proxy service an opportunity to reconsider the proxy to use.
-  // The |results| parameter contains the results returned by an earlier call
-  // to ResolveProxy.  The |net_error| parameter contains the network error
-  // code associated with the failure. See "net/base/net_error_list.h" for a
-  // list of possible values. The semantics of this call are otherwise
-  // similar to ResolveProxy.
-  //
-  // NULL can be passed for |request| if the caller will not need to
-  // cancel the request.
-  //
-  // Returns ERR_FAILED if there is not another proxy config to try.
-  //
-  // Profiling information for the request is saved to |net_log| if non-NULL.
-  int ReconsiderProxyAfterError(const GURL& url,
-                                const std::string& method,
-                                int net_error,
-                                ProxyInfo* results,
-                                const CompletionCallback& callback,
-                                Request** request,
-                                ProxyDelegate* proxy_delegate,
-                                const NetLogWithSource& net_log);
-
   // Explicitly trigger proxy fallback for the given |results| by updating our
   // list of bad proxies to include the first entry of |results|, and,
   // additional bad proxies (can be none). Will retry after |retry_delay| if
@@ -235,14 +213,12 @@
       std::unique_ptr<ProxyConfigService> new_proxy_config_service);
 
   // Returns the last configuration fetched from ProxyConfigService.
-  const ProxyConfig& fetched_config() {
+  const base::Optional<ProxyConfig>& fetched_config() const {
     return fetched_config_;
   }
 
   // Returns the current configuration being used by ProxyConfigService.
-  const ProxyConfig& config() const {
-    return config_;
-  }
+  const base::Optional<ProxyConfig>& config() const { return config_; }
 
   // Returns the map of proxies which have been marked as "bad".
   const ProxyRetryInfoMap& proxy_retry_info() const {
@@ -424,11 +400,10 @@
   // The effective configuration is what we condense the original fetched
   // settings to after testing the various automatic settings (auto-detect
   // and custom PAC url).
-  ProxyConfig fetched_config_;
-  ProxyConfig config_;
-
-  // Increasing ID to give to the next ProxyConfig that we set.
-  int next_config_id_;
+  //
+  // These are "optional" as their value remains unset while being calculated.
+  base::Optional<ProxyConfig> fetched_config_;
+  base::Optional<ProxyConfig> config_;
 
   // The time when the proxy configuration was last read from the system.
   base::TimeTicks config_last_update_time_;
diff --git a/net/proxy_resolution/proxy_service_unittest.cc b/net/proxy_resolution/proxy_service_unittest.cc
index a98bd01..4691327 100644
--- a/net/proxy_resolution/proxy_service_unittest.cc
+++ b/net/proxy_resolution/proxy_service_unittest.cc
@@ -389,11 +389,7 @@
   EXPECT_THAT(rv, IsOk());
   EXPECT_EQ("badproxy:8080", info.proxy_server().ToURI());
 
-  TestCompletionCallback callback2;
-  rv = service.ReconsiderProxyAfterError(
-      url, std::string(), ERR_PROXY_CONNECTION_FAILED, &info,
-      callback2.callback(), nullptr, nullptr, NetLogWithSource());
-  EXPECT_THAT(rv, IsOk());
+  EXPECT_TRUE(info.Fallback(ERR_PROXY_CONNECTION_FAILED, NetLogWithSource()));
   EXPECT_EQ("foopy1:8080", info.proxy_server().ToURI());
 
   service.ReportSuccess(info, nullptr);
@@ -609,14 +605,7 @@
   // Now, imagine that connecting to foopy:8080 fails: there is nothing
   // left to fallback to, since our proxy list was NOT terminated by
   // DIRECT.
-  TestResolveProxyDelegate proxy_delegate;
-  TestCompletionCallback callback2;
-  ProxyServer expected_proxy_server = info.proxy_server();
-  rv = service.ReconsiderProxyAfterError(
-      url, "GET", ERR_PROXY_CONNECTION_FAILED, &info, callback2.callback(),
-      nullptr, &proxy_delegate, NetLogWithSource());
-  // ReconsiderProxyAfterError returns error indicating nothing left.
-  EXPECT_THAT(rv, IsError(ERR_FAILED));
+  EXPECT_FALSE(info.Fallback(ERR_PROXY_CONNECTION_FAILED, NetLogWithSource()));
   EXPECT_TRUE(info.is_empty());
 }
 
@@ -656,7 +645,6 @@
   // DIRECT.
   EXPECT_TRUE(info.is_direct());
   EXPECT_TRUE(info.did_use_pac_script());
-  EXPECT_EQ(1, info.config_id());
 
   EXPECT_FALSE(info.proxy_resolve_start_time().is_null());
   EXPECT_FALSE(info.proxy_resolve_end_time().is_null());
@@ -714,41 +702,21 @@
   EXPECT_TRUE(info.is_direct());
 
   // Fallback 1.
-  TestCompletionCallback callback2;
-  rv = service.ReconsiderProxyAfterError(
-      url, std::string(), ERR_PROXY_CONNECTION_FAILED, &info,
-      callback2.callback(), nullptr, nullptr, NetLogWithSource());
-  EXPECT_THAT(rv, IsOk());
+  EXPECT_TRUE(info.Fallback(ERR_PROXY_CONNECTION_FAILED, NetLogWithSource()));
   EXPECT_FALSE(info.is_direct());
   EXPECT_EQ("foobar:10", info.proxy_server().ToURI());
 
   // Fallback 2.
-  TestResolveProxyDelegate proxy_delegate;
-  ProxyServer expected_proxy_server3 = info.proxy_server();
-  TestCompletionCallback callback3;
-  rv = service.ReconsiderProxyAfterError(
-      url, "GET", ERR_PROXY_CONNECTION_FAILED, &info, callback3.callback(),
-      nullptr, &proxy_delegate, NetLogWithSource());
-  EXPECT_THAT(rv, IsOk());
+  EXPECT_TRUE(info.Fallback(ERR_PROXY_CONNECTION_FAILED, NetLogWithSource()));
   EXPECT_TRUE(info.is_direct());
 
   // Fallback 3.
-  ProxyServer expected_proxy_server4 = info.proxy_server();
-  TestCompletionCallback callback4;
-  rv = service.ReconsiderProxyAfterError(
-      url, "GET", ERR_PROXY_CONNECTION_FAILED, &info, callback4.callback(),
-      nullptr, &proxy_delegate, NetLogWithSource());
-  EXPECT_THAT(rv, IsOk());
+  EXPECT_TRUE(info.Fallback(ERR_PROXY_CONNECTION_FAILED, NetLogWithSource()));
   EXPECT_FALSE(info.is_direct());
   EXPECT_EQ("foobar:20", info.proxy_server().ToURI());
 
   // Fallback 4 -- Nothing to fall back to!
-  ProxyServer expected_proxy_server5 = info.proxy_server();
-  TestCompletionCallback callback5;
-  rv = service.ReconsiderProxyAfterError(
-      url, "GET", ERR_PROXY_CONNECTION_FAILED, &info, callback5.callback(),
-      nullptr, &proxy_delegate, NetLogWithSource());
-  EXPECT_THAT(rv, IsError(ERR_FAILED));
+  EXPECT_FALSE(info.Fallback(ERR_PROXY_CONNECTION_FAILED, NetLogWithSource()));
   EXPECT_TRUE(info.is_empty());
 }
 
@@ -1196,11 +1164,7 @@
   base::TimeTicks proxy_resolve_end_time = info.proxy_resolve_end_time();
 
   // Fake an error on the proxy.
-  TestCompletionCallback callback2;
-  rv = service.ReconsiderProxyAfterError(
-      url, std::string(), ERR_PROXY_CONNECTION_FAILED, &info,
-      callback2.callback(), nullptr, nullptr, NetLogWithSource());
-  EXPECT_THAT(rv, IsOk());
+  EXPECT_TRUE(info.Fallback(ERR_PROXY_CONNECTION_FAILED, NetLogWithSource()));
 
   // Proxy times should not have been modified by fallback.
   EXPECT_EQ(proxy_resolve_start_time, info.proxy_resolve_start_time());
@@ -1243,30 +1207,18 @@
   proxy_resolve_end_time = info.proxy_resolve_end_time();
 
   // We fake another error. It should now try the third one.
-  TestCompletionCallback callback4;
-  rv = service.ReconsiderProxyAfterError(
-      url, std::string(), ERR_PROXY_CONNECTION_FAILED, &info,
-      callback4.callback(), nullptr, nullptr, NetLogWithSource());
-  EXPECT_THAT(rv, IsOk());
+  EXPECT_TRUE(info.Fallback(ERR_PROXY_CONNECTION_FAILED, NetLogWithSource()));
   EXPECT_EQ("foopy2:9090", info.proxy_server().ToURI());
 
   // We fake another error. At this point we have tried all of the
   // proxy servers we thought were valid; next we try the proxy server
   // that was in our bad proxies map (foopy1:8080).
-  TestCompletionCallback callback5;
-  rv = service.ReconsiderProxyAfterError(
-      url, std::string(), ERR_PROXY_CONNECTION_FAILED, &info,
-      callback5.callback(), nullptr, nullptr, NetLogWithSource());
-  EXPECT_THAT(rv, IsOk());
+  EXPECT_TRUE(info.Fallback(ERR_PROXY_CONNECTION_FAILED, NetLogWithSource()));
   EXPECT_EQ("foopy1:8080", info.proxy_server().ToURI());
 
   // Fake another error, the last proxy is gone, the list should now be empty,
   // so there is nothing left to try.
-  TestCompletionCallback callback6;
-  rv = service.ReconsiderProxyAfterError(
-      url, std::string(), ERR_PROXY_CONNECTION_FAILED, &info,
-      callback6.callback(), nullptr, nullptr, NetLogWithSource());
-  EXPECT_THAT(rv, IsError(ERR_FAILED));
+  EXPECT_FALSE(info.Fallback(ERR_PROXY_CONNECTION_FAILED, NetLogWithSource()));
   EXPECT_FALSE(info.is_direct());
   EXPECT_TRUE(info.is_empty());
 
@@ -1340,21 +1292,13 @@
   EXPECT_EQ("foopy1:8080", info.proxy_server().ToURI());
 
   // Fake an error on the proxy.
-  TestCompletionCallback callback2;
-  rv = service.ReconsiderProxyAfterError(
-      url, std::string(), ERR_PROXY_CONNECTION_FAILED, &info,
-      callback2.callback(), nullptr, nullptr, NetLogWithSource());
-  EXPECT_THAT(rv, IsOk());
+  EXPECT_TRUE(info.Fallback(ERR_PROXY_CONNECTION_FAILED, NetLogWithSource()));
 
   // Now we get back the second proxy.
   EXPECT_EQ("foopy2:9090", info.proxy_server().ToURI());
 
   // Fake an error on this proxy as well.
-  TestCompletionCallback callback3;
-  rv = service.ReconsiderProxyAfterError(
-      url, std::string(), ERR_PROXY_CONNECTION_FAILED, &info,
-      callback3.callback(), nullptr, nullptr, NetLogWithSource());
-  EXPECT_THAT(rv, IsOk());
+  EXPECT_TRUE(info.Fallback(ERR_PROXY_CONNECTION_FAILED, NetLogWithSource()));
 
   // Finally, we get back DIRECT.
   EXPECT_TRUE(info.is_direct());
@@ -1364,116 +1308,9 @@
   EXPECT_LE(info.proxy_resolve_start_time(), info.proxy_resolve_end_time());
 
   // Now we tell the proxy service that even DIRECT failed.
-  TestCompletionCallback callback4;
-  rv = service.ReconsiderProxyAfterError(
-      url, std::string(), ERR_PROXY_CONNECTION_FAILED, &info,
-      callback4.callback(), nullptr, nullptr, NetLogWithSource());
   // There was nothing left to try after DIRECT, so we are out of
   // choices.
-  EXPECT_THAT(rv, IsError(ERR_FAILED));
-}
-
-TEST_F(ProxyServiceTest, ProxyFallback_NewSettings) {
-  // Test proxy failover when new settings are available.
-
-  MockProxyConfigService* config_service =
-      new MockProxyConfigService("http://foopy/proxy.pac");
-
-  MockAsyncProxyResolver resolver;
-  MockAsyncProxyResolverFactory* factory =
-      new MockAsyncProxyResolverFactory(false);
-
-  ProxyResolutionService service(base::WrapUnique(config_service),
-                                 base::WrapUnique(factory), nullptr);
-
-  GURL url("http://www.google.com/");
-
-  // Get the proxy information.
-  ProxyInfo info;
-  TestCompletionCallback callback1;
-  int rv = service.ResolveProxy(url, std::string(), &info, callback1.callback(),
-                                nullptr, nullptr, NetLogWithSource());
-  EXPECT_THAT(rv, IsError(ERR_IO_PENDING));
-
-  EXPECT_EQ(GURL("http://foopy/proxy.pac"),
-            factory->pending_requests()[0]->script_data()->url());
-  factory->pending_requests()[0]->CompleteNowWithForwarder(OK, &resolver);
-
-  ASSERT_EQ(1u, resolver.pending_jobs().size());
-  EXPECT_EQ(url, resolver.pending_jobs()[0]->url());
-
-  // Set the result in proxy resolver.
-  resolver.pending_jobs()[0]->results()->UseNamedProxy(
-      "foopy1:8080;foopy2:9090");
-  resolver.pending_jobs()[0]->CompleteNow(OK);
-
-  // The first item is valid.
-  EXPECT_THAT(callback1.WaitForResult(), IsOk());
-  EXPECT_FALSE(info.is_direct());
-  EXPECT_EQ("foopy1:8080", info.proxy_server().ToURI());
-
-  // Fake an error on the proxy, and also a new configuration on the proxy.
-  config_service->SetConfig(
-      ProxyConfig::CreateFromCustomPacURL(GURL("http://foopy-new/proxy.pac")));
-
-  TestCompletionCallback callback2;
-  rv = service.ReconsiderProxyAfterError(
-      url, std::string(), ERR_PROXY_CONNECTION_FAILED, &info,
-      callback2.callback(), nullptr, nullptr, NetLogWithSource());
-  EXPECT_THAT(rv, IsError(ERR_IO_PENDING));
-
-  EXPECT_EQ(GURL("http://foopy-new/proxy.pac"),
-            factory->pending_requests()[0]->script_data()->url());
-  factory->pending_requests()[0]->CompleteNowWithForwarder(OK, &resolver);
-
-  ASSERT_EQ(1u, resolver.pending_jobs().size());
-  EXPECT_EQ(url, resolver.pending_jobs()[0]->url());
-
-  resolver.pending_jobs()[0]->results()->UseNamedProxy(
-      "foopy1:8080;foopy2:9090");
-  resolver.pending_jobs()[0]->CompleteNow(OK);
-
-  // The first proxy is still there since the configuration changed.
-  EXPECT_THAT(callback2.WaitForResult(), IsOk());
-  EXPECT_EQ("foopy1:8080", info.proxy_server().ToURI());
-
-  // We fake another error. It should now ignore the first one.
-  TestCompletionCallback callback3;
-  rv = service.ReconsiderProxyAfterError(
-      url, std::string(), ERR_PROXY_CONNECTION_FAILED, &info,
-      callback3.callback(), nullptr, nullptr, NetLogWithSource());
-  EXPECT_THAT(rv, IsOk());
-  EXPECT_EQ("foopy2:9090", info.proxy_server().ToURI());
-
-  // We simulate a new configuration.
-  config_service->SetConfig(
-      ProxyConfig::CreateFromCustomPacURL(
-          GURL("http://foopy-new2/proxy.pac")));
-
-  // We fake another error. It should go back to the first proxy.
-  TestCompletionCallback callback4;
-  rv = service.ReconsiderProxyAfterError(
-      url, std::string(), ERR_PROXY_CONNECTION_FAILED, &info,
-      callback4.callback(), nullptr, nullptr, NetLogWithSource());
-  EXPECT_THAT(rv, IsError(ERR_IO_PENDING));
-
-  EXPECT_EQ(GURL("http://foopy-new2/proxy.pac"),
-            factory->pending_requests()[0]->script_data()->url());
-  factory->pending_requests()[0]->CompleteNowWithForwarder(OK, &resolver);
-
-  ASSERT_EQ(1u, resolver.pending_jobs().size());
-  EXPECT_EQ(url, resolver.pending_jobs()[0]->url());
-
-  resolver.pending_jobs()[0]->results()->UseNamedProxy(
-      "foopy1:8080;foopy2:9090");
-  resolver.pending_jobs()[0]->CompleteNow(OK);
-
-  EXPECT_THAT(callback4.WaitForResult(), IsOk());
-  EXPECT_EQ("foopy1:8080", info.proxy_server().ToURI());
-
-  EXPECT_FALSE(info.proxy_resolve_start_time().is_null());
-  EXPECT_FALSE(info.proxy_resolve_end_time().is_null());
-  EXPECT_LE(info.proxy_resolve_start_time(), info.proxy_resolve_end_time());
+  EXPECT_FALSE(info.Fallback(ERR_PROXY_CONNECTION_FAILED, NetLogWithSource()));
 }
 
 TEST_F(ProxyServiceTest, ProxyFallback_BadConfig) {
@@ -1514,11 +1351,7 @@
   EXPECT_EQ("foopy1:8080", info.proxy_server().ToURI());
 
   // Fake a proxy error.
-  TestCompletionCallback callback2;
-  rv = service.ReconsiderProxyAfterError(
-      url, std::string(), ERR_PROXY_CONNECTION_FAILED, &info,
-      callback2.callback(), nullptr, nullptr, NetLogWithSource());
-  EXPECT_THAT(rv, IsOk());
+  EXPECT_TRUE(info.Fallback(ERR_PROXY_CONNECTION_FAILED, NetLogWithSource()));
 
   // The first proxy is ignored, and the second one is selected.
   EXPECT_FALSE(info.is_direct());
@@ -1548,9 +1381,8 @@
   // "just work" the next time we call it.
   ProxyInfo info3;
   TestCompletionCallback callback4;
-  rv = service.ReconsiderProxyAfterError(
-      url, std::string(), ERR_PROXY_CONNECTION_FAILED, &info3,
-      callback4.callback(), nullptr, nullptr, NetLogWithSource());
+  rv = service.ResolveProxy(url, std::string(), &info3, callback4.callback(),
+                            nullptr, nullptr, NetLogWithSource());
   EXPECT_THAT(rv, IsError(ERR_IO_PENDING));
 
   ASSERT_EQ(1u, resolver.pending_jobs().size());
@@ -1560,7 +1392,7 @@
       "foopy1:8080;foopy2:9090");
   resolver.pending_jobs()[0]->CompleteNow(OK);
 
-  // The first proxy is not there since the it was added to the bad proxies
+  // The first proxy is not there since it was added to the bad proxies
   // list by the earlier ReconsiderProxyAfterError().
   EXPECT_THAT(callback4.WaitForResult(), IsOk());
   EXPECT_FALSE(info3.is_direct());
@@ -1612,11 +1444,7 @@
   EXPECT_EQ("foopy1:8080", info.proxy_server().ToURI());
 
   // Fake a proxy error.
-  TestCompletionCallback callback2;
-  rv = service.ReconsiderProxyAfterError(
-      url, std::string(), ERR_PROXY_CONNECTION_FAILED, &info,
-      callback2.callback(), nullptr, nullptr, NetLogWithSource());
-  EXPECT_THAT(rv, IsOk());
+  EXPECT_TRUE(info.Fallback(ERR_PROXY_CONNECTION_FAILED, NetLogWithSource()));
 
   // The first proxy is ignored, and the second one is selected.
   EXPECT_FALSE(info.is_direct());
@@ -1647,9 +1475,8 @@
   // "just work" the next time we call it.
   ProxyInfo info3;
   TestCompletionCallback callback4;
-  rv = service.ReconsiderProxyAfterError(
-      url, std::string(), ERR_PROXY_CONNECTION_FAILED, &info3,
-      callback4.callback(), nullptr, nullptr, NetLogWithSource());
+  rv = service.ResolveProxy(url, std::string(), &info3, callback4.callback(),
+                            nullptr, nullptr, NetLogWithSource());
   EXPECT_THAT(rv, IsError(ERR_IO_PENDING));
 
   ASSERT_EQ(1u, resolver.pending_jobs().size());
diff --git a/net/reporting/reporting_delegate.cc b/net/reporting/reporting_delegate.cc
index 42006db..8788137a 100644
--- a/net/reporting/reporting_delegate.cc
+++ b/net/reporting/reporting_delegate.cc
@@ -4,6 +4,7 @@
 
 #include "net/reporting/reporting_delegate.h"
 
+#include "base/json/json_reader.h"
 #include "net/base/network_delegate.h"
 #include "net/url_request/url_request_context.h"
 
@@ -42,6 +43,16 @@
            network_delegate()->CanUseReportingClient(origin, endpoint);
   }
 
+  void ParseJson(const std::string& unsafe_json,
+                 const JsonSuccessCallback& success_callback,
+                 const JsonFailureCallback& failure_callback) const override {
+    std::unique_ptr<base::Value> value = base::JSONReader::Read(unsafe_json);
+    if (value)
+      success_callback.Run(std::move(value));
+    else
+      failure_callback.Run();
+  }
+
  private:
   const NetworkDelegate* network_delegate() const {
     return request_context_->network_delegate();
diff --git a/net/reporting/reporting_delegate.h b/net/reporting/reporting_delegate.h
index d2052f6e..ba8d7a9 100644
--- a/net/reporting/reporting_delegate.h
+++ b/net/reporting/reporting_delegate.h
@@ -7,7 +7,9 @@
 
 #include <memory>
 
+#include "base/callback.h"
 #include "base/macros.h"
+#include "base/values.h"
 #include "net/base/net_export.h"
 
 class GURL;
@@ -40,6 +42,22 @@
   virtual bool CanUseClient(const url::Origin& origin,
                             const GURL& endpoint) const = 0;
 
+  // TODO(crbug.com/811485): Use OnceCallback/Closure.
+  using JsonSuccessCallback =
+      base::RepeatingCallback<void(std::unique_ptr<base::Value>)>;
+  using JsonFailureCallback = base::RepeatingClosure;
+
+  // Parses JSON. How safely, and using what mechanism, is up to the embedder,
+  // but //components/data_decoder is recommended if available.
+  //
+  // Exactly one callback should be made, either to |success_callback| (with the
+  // parsed value) if parsing succeeded or to |failure_callback| if parsing
+  // failed. The callbacks may be called either synchronously or
+  // asynchronously.
+  virtual void ParseJson(const std::string& unsafe_json,
+                         const JsonSuccessCallback& success_callback,
+                         const JsonFailureCallback& failure_callback) const = 0;
+
   static std::unique_ptr<ReportingDelegate> Create(
       URLRequestContext* request_context);
 };
diff --git a/net/reporting/reporting_header_parser.cc b/net/reporting/reporting_header_parser.cc
index 5512a5b..df3f00e0 100644
--- a/net/reporting/reporting_header_parser.cc
+++ b/net/reporting/reporting_header_parser.cc
@@ -6,6 +6,7 @@
 
 #include <string>
 
+#include "base/bind.h"
 #include "base/json/json_reader.h"
 #include "base/logging.h"
 #include "base/metrics/histogram_macros.h"
@@ -172,18 +173,16 @@
 }
 
 // static
+void ReportingHeaderParser::RecordHeaderDiscardedForInvalidJson() {
+  RecordHeaderOutcome(HeaderOutcome::DISCARDED_INVALID_JSON);
+}
+
+// static
 void ReportingHeaderParser::ParseHeader(ReportingContext* context,
                                         const GURL& url,
-                                        const std::string& json_value) {
+                                        std::unique_ptr<base::Value> value) {
   DCHECK(url.SchemeIsCryptographic());
 
-  std::unique_ptr<base::Value> value =
-      base::JSONReader::Read("[" + json_value + "]");
-  if (!value) {
-    RecordHeaderOutcome(HeaderOutcome::DISCARDED_INVALID_JSON);
-    return;
-  }
-
   const base::ListValue* endpoint_list = nullptr;
   bool is_list = value->GetAsList(&endpoint_list);
   DCHECK(is_list);
diff --git a/net/reporting/reporting_header_parser.h b/net/reporting/reporting_header_parser.h
index c94f053..94a29be 100644
--- a/net/reporting/reporting_header_parser.h
+++ b/net/reporting/reporting_header_parser.h
@@ -5,13 +5,17 @@
 #ifndef NET_REPORTING_REPORTING_HEADER_PARSER_H_
 #define NET_REPORTING_REPORTING_HEADER_PARSER_H_
 
-#include <string>
+#include <memory>
 
 #include "base/macros.h"
 #include "net/base/net_export.h"
 
 class GURL;
 
+namespace base {
+class Value;
+}  // namespace base
+
 namespace net {
 
 class ReportingContext;
@@ -21,10 +25,11 @@
   static void RecordHeaderDiscardedForNoReportingService();
   static void RecordHeaderDiscardedForInvalidSSLInfo();
   static void RecordHeaderDiscardedForCertStatusError();
+  static void RecordHeaderDiscardedForInvalidJson();
 
   static void ParseHeader(ReportingContext* context,
                           const GURL& url,
-                          const std::string& json_value);
+                          std::unique_ptr<base::Value> value);
 
  private:
   DISALLOW_IMPLICIT_CONSTRUCTORS(ReportingHeaderParser);
diff --git a/net/reporting/reporting_header_parser_fuzzer.cc b/net/reporting/reporting_header_parser_fuzzer.cc
index 302cc8fb..2e1dfd1 100644
--- a/net/reporting/reporting_header_parser_fuzzer.cc
+++ b/net/reporting/reporting_header_parser_fuzzer.cc
@@ -2,9 +2,14 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+#include <memory>
+#include <utility>
+
+#include "base/json/json_reader.h"
 #include "base/time/default_clock.h"
 #include "base/time/default_tick_clock.h"
 #include "base/time/time.h"
+#include "base/values.h"
 #include "net/reporting/reporting_cache.h"
 #include "net/reporting/reporting_client.h"
 #include "net/reporting/reporting_header_parser.h"
@@ -24,12 +29,16 @@
 
 namespace net_reporting_header_parser_fuzzer {
 
-void FuzzReportingHeaderParser(const std::string& data,
-                              const net::ReportingPolicy& policy) {
+void FuzzReportingHeaderParser(const std::string& data_json,
+                               const net::ReportingPolicy& policy) {
   net::TestReportingContext context(base::DefaultClock::GetInstance(),
                                     base::DefaultTickClock::GetInstance(),
                                     policy);
-  net::ReportingHeaderParser::ParseHeader(&context, kUrl_, data.c_str());
+  std::unique_ptr<base::Value> data_value = base::JSONReader::Read(data_json);
+  if (!data_value)
+    return;
+  net::ReportingHeaderParser::ParseHeader(&context, kUrl_,
+                                          std::move(data_value));
   std::vector<const net::ReportingClient*> clients;
   context.cache()->GetClients(&clients);
   if (clients.empty()) {
diff --git a/net/reporting/reporting_header_parser_unittest.cc b/net/reporting/reporting_header_parser_unittest.cc
index 151675e..d09f0fd 100644
--- a/net/reporting/reporting_header_parser_unittest.cc
+++ b/net/reporting/reporting_header_parser_unittest.cc
@@ -7,6 +7,7 @@
 #include <string>
 #include <vector>
 
+#include "base/json/json_reader.h"
 #include "base/test/simple_test_tick_clock.h"
 #include "base/time/time.h"
 #include "base/values.h"
@@ -22,6 +23,13 @@
 
 class ReportingHeaderParserTest : public ReportingTestBase {
  protected:
+  void ParseHeader(const GURL& url, const std::string& json) {
+    std::unique_ptr<base::Value> value =
+        base::JSONReader::Read("[" + json + "]");
+    if (value)
+      ReportingHeaderParser::ParseHeader(context(), url, std::move(value));
+  }
+
   const GURL kUrl_ = GURL("https://origin/path");
   const url::Origin kOrigin_ = url::Origin::Create(GURL("https://origin/"));
   const GURL kEndpoint_ = GURL("https://endpoint/");
@@ -29,6 +37,10 @@
   const std::string kType_ = "type";
 };
 
+// TODO(juliatuttle): Ideally these tests should be expecting that JSON parsing
+// (and therefore header parsing) may happen asynchronously, but the entire
+// pipeline is also tested by NetworkErrorLoggingEndToEndTest.
+
 TEST_F(ReportingHeaderParserTest, Invalid) {
   static const struct {
     const char* header_value;
@@ -65,8 +77,7 @@
 
   for (size_t i = 0; i < arraysize(kInvalidHeaderTestCases); ++i) {
     auto& test_case = kInvalidHeaderTestCases[i];
-    ReportingHeaderParser::ParseHeader(context(), kUrl_,
-                                       test_case.header_value);
+    ParseHeader(kUrl_, test_case.header_value);
 
     std::vector<const ReportingClient*> clients;
     cache()->GetClients(&clients);
@@ -77,9 +88,8 @@
 }
 
 TEST_F(ReportingHeaderParserTest, Valid) {
-  ReportingHeaderParser::ParseHeader(
-      context(), kUrl_,
-      "{\"url\":\"" + kEndpoint_.spec() + "\",\"max-age\":86400}");
+  ParseHeader(kUrl_,
+              "{\"url\":\"" + kEndpoint_.spec() + "\",\"max-age\":86400}");
 
   const ReportingClient* client =
       FindClientInCache(cache(), kOrigin_, kEndpoint_);
@@ -98,18 +108,15 @@
       tick_clock()->NowTicks() + base::TimeDelta::FromDays(1),
       ReportingClient::kDefaultPriority, ReportingClient::kDefaultWeight);
 
-  ReportingHeaderParser::ParseHeader(
-      context(), kUrl_,
-      "{\"url\":\"" + kEndpoint_.spec() + "\",\"max-age\":0}");
+  ParseHeader(kUrl_, "{\"url\":\"" + kEndpoint_.spec() + "\",\"max-age\":0}");
 
   EXPECT_EQ(nullptr, FindClientInCache(cache(), kOrigin_, kEndpoint_));
 }
 
 TEST_F(ReportingHeaderParserTest, Subdomains) {
-  ReportingHeaderParser::ParseHeader(context(), kUrl_,
-                                     "{\"url\":\"" + kEndpoint_.spec() +
-                                         "\",\"max-age\":86400,"
-                                         "\"includeSubdomains\":true}");
+  ParseHeader(kUrl_, "{\"url\":\"" + kEndpoint_.spec() +
+                         "\",\"max-age\":86400,"
+                         "\"includeSubdomains\":true}");
 
   const ReportingClient* client =
       FindClientInCache(cache(), kOrigin_, kEndpoint_);
@@ -118,10 +125,9 @@
 }
 
 TEST_F(ReportingHeaderParserTest, PriorityPositive) {
-  ReportingHeaderParser::ParseHeader(context(), kUrl_,
-                                     "{\"url\":\"" + kEndpoint_.spec() +
-                                         "\",\"max-age\":86400,"
-                                         "\"priority\":2}");
+  ParseHeader(kUrl_, "{\"url\":\"" + kEndpoint_.spec() +
+                         "\",\"max-age\":86400,"
+                         "\"priority\":2}");
 
   const ReportingClient* client =
       FindClientInCache(cache(), kOrigin_, kEndpoint_);
@@ -130,10 +136,9 @@
 }
 
 TEST_F(ReportingHeaderParserTest, PriorityNegative) {
-  ReportingHeaderParser::ParseHeader(context(), kUrl_,
-                                     "{\"url\":\"" + kEndpoint_.spec() +
-                                         "\",\"max-age\":86400,"
-                                         "\"priority\":-2}");
+  ParseHeader(kUrl_, "{\"url\":\"" + kEndpoint_.spec() +
+                         "\",\"max-age\":86400,"
+                         "\"priority\":-2}");
 
   const ReportingClient* client =
       FindClientInCache(cache(), kOrigin_, kEndpoint_);
@@ -142,10 +147,9 @@
 }
 
 TEST_F(ReportingHeaderParserTest, Weight) {
-  ReportingHeaderParser::ParseHeader(context(), kUrl_,
-                                     "{\"url\":\"" + kEndpoint_.spec() +
-                                         "\",\"max-age\":86400,"
-                                         "\"weight\":3}");
+  ParseHeader(kUrl_, "{\"url\":\"" + kEndpoint_.spec() +
+                         "\",\"max-age\":86400,"
+                         "\"weight\":3}");
 
   const ReportingClient* client =
       FindClientInCache(cache(), kOrigin_, kEndpoint_);
@@ -156,15 +160,13 @@
 TEST_F(ReportingHeaderParserTest, RemoveOld) {
   static const GURL kDifferentEndpoint_ = GURL("https://endpoint2/");
 
-  ReportingHeaderParser::ParseHeader(
-      context(), kUrl_,
-      "{\"url\":\"" + kEndpoint_.spec() + "\",\"max-age\":86400}");
+  ParseHeader(kUrl_,
+              "{\"url\":\"" + kEndpoint_.spec() + "\",\"max-age\":86400}");
 
   EXPECT_TRUE(FindClientInCache(cache(), kOrigin_, kEndpoint_));
 
-  ReportingHeaderParser::ParseHeader(
-      context(), kUrl_,
-      "{\"url\":\"" + kDifferentEndpoint_.spec() + "\",\"max-age\":86400}");
+  ParseHeader(kUrl_, "{\"url\":\"" + kDifferentEndpoint_.spec() +
+                         "\",\"max-age\":86400}");
 
   EXPECT_FALSE(FindClientInCache(cache(), kOrigin_, kEndpoint_));
   EXPECT_TRUE(FindClientInCache(cache(), kOrigin_, kDifferentEndpoint_));
diff --git a/net/reporting/reporting_service.cc b/net/reporting/reporting_service.cc
index 46fbba8e..5137082 100644
--- a/net/reporting/reporting_service.cc
+++ b/net/reporting/reporting_service.cc
@@ -8,6 +8,7 @@
 
 #include "base/bind.h"
 #include "base/macros.h"
+#include "base/memory/weak_ptr.h"
 #include "base/time/tick_clock.h"
 #include "base/time/time.h"
 #include "base/values.h"
@@ -26,7 +27,7 @@
 class ReportingServiceImpl : public ReportingService {
  public:
   ReportingServiceImpl(std::unique_ptr<ReportingContext> context)
-      : context_(std::move(context)) {}
+      : context_(std::move(context)), weak_factory_(this) {}
 
   // ReportingService implementation:
 
@@ -48,7 +49,12 @@
 
   void ProcessHeader(const GURL& url,
                      const std::string& header_value) override {
-    ReportingHeaderParser::ParseHeader(context_.get(), url, header_value);
+    context_->delegate()->ParseJson(
+        "[" + header_value + "]",
+        base::BindRepeating(&ReportingServiceImpl::ProcessHeaderValue,
+                            weak_factory_.GetWeakPtr(), url),
+        base::BindRepeating(
+            &ReportingHeaderParser::RecordHeaderDiscardedForInvalidJson));
   }
 
   void RemoveBrowsingData(int data_type_mask,
@@ -67,7 +73,12 @@
   }
 
  private:
+  void ProcessHeaderValue(const GURL& url, std::unique_ptr<base::Value> value) {
+    ReportingHeaderParser::ParseHeader(context_.get(), url, std::move(value));
+  }
+
   std::unique_ptr<ReportingContext> context_;
+  base::WeakPtrFactory<ReportingServiceImpl> weak_factory_;
 
   DISALLOW_COPY_AND_ASSIGN(ReportingServiceImpl);
 };
diff --git a/net/reporting/reporting_test_util.cc b/net/reporting/reporting_test_util.cc
index 17671b21a..4dfc21d6 100644
--- a/net/reporting/reporting_test_util.cc
+++ b/net/reporting/reporting_test_util.cc
@@ -132,6 +132,17 @@
   return true;
 }
 
+void TestReportingDelegate::ParseJson(
+    const std::string& unsafe_json,
+    const JsonSuccessCallback& success_callback,
+    const JsonFailureCallback& failure_callback) const {
+  std::unique_ptr<base::Value> value = base::JSONReader::Read(unsafe_json);
+  if (value)
+    success_callback.Run(std::move(value));
+  else
+    failure_callback.Run();
+}
+
 TestReportingContext::TestReportingContext(base::Clock* clock,
                                            base::TickClock* tick_clock,
                                            const ReportingPolicy& policy)
diff --git a/net/reporting/reporting_test_util.h b/net/reporting/reporting_test_util.h
index f2dff8f..fe56e3aa 100644
--- a/net/reporting/reporting_test_util.h
+++ b/net/reporting/reporting_test_util.h
@@ -99,6 +99,10 @@
   bool CanUseClient(const url::Origin& origin,
                     const GURL& endpoint) const override;
 
+  void ParseJson(const std::string& unsafe_json,
+                 const JsonSuccessCallback& success_callback,
+                 const JsonFailureCallback& failure_callback) const override;
+
  private:
   DISALLOW_COPY_AND_ASSIGN(TestReportingDelegate);
 };
diff --git a/net/spdy/chromium/spdy_http_utils.cc b/net/spdy/chromium/spdy_http_utils.cc
index 4884872..0cb8c3a 100644
--- a/net/spdy/chromium/spdy_http_utils.cc
+++ b/net/spdy/chromium/spdy_http_utils.cc
@@ -109,6 +109,28 @@
   }
 }
 
+void CreateSpdyHeadersFromHttpRequestForWebSocket(
+    const GURL& url,
+    const HttpRequestHeaders& request_headers,
+    SpdyHeaderBlock* headers) {
+  (*headers)[kHttp2MethodHeader] = "CONNECT";
+  (*headers)[kHttp2AuthorityHeader] = GetHostAndPort(url);
+  (*headers)[kHttp2SchemeHeader] = "https";
+  (*headers)[kHttp2PathHeader] = url.PathForRequest();
+  (*headers)[kHttp2ProtocolHeader] = "websocket";
+
+  HttpRequestHeaders::Iterator it(request_headers);
+  while (it.GetNext()) {
+    SpdyString name = base::ToLowerASCII(it.name());
+    if (name.empty() || name[0] == ':' || name == "upgrade" ||
+        name == "connection" || name == "proxy-connection" ||
+        name == "transfer-encoding" || name == "host") {
+      continue;
+    }
+    AddSpdyHeader(name, it.value(), headers);
+  }
+}
+
 static_assert(HIGHEST - LOWEST < 4 && HIGHEST - MINIMUM_PRIORITY < 6,
               "request priority incompatible with spdy");
 
diff --git a/net/spdy/chromium/spdy_http_utils.h b/net/spdy/chromium/spdy_http_utils.h
index c509939b..fa10b91 100644
--- a/net/spdy/chromium/spdy_http_utils.h
+++ b/net/spdy/chromium/spdy_http_utils.h
@@ -33,6 +33,13 @@
     const HttpRequestHeaders& request_headers,
     SpdyHeaderBlock* headers);
 
+// Create a SpdyHeaderBlock from HttpRequestInfo and HttpRequestHeaders for a
+// WebSockets over HTTP/2 request.
+NET_EXPORT void CreateSpdyHeadersFromHttpRequestForWebSocket(
+    const GURL& url,
+    const HttpRequestHeaders& request_headers,
+    SpdyHeaderBlock* headers);
+
 // Create HttpRequestHeaders from SpdyHeaderBlock.
 NET_EXPORT void ConvertHeaderBlockToHttpRequestHeaders(
     const SpdyHeaderBlock& spdy_headers,
diff --git a/net/spdy/chromium/spdy_network_transaction_unittest.cc b/net/spdy/chromium/spdy_network_transaction_unittest.cc
index c866451..99e86e55 100644
--- a/net/spdy/chromium/spdy_network_transaction_unittest.cc
+++ b/net/spdy/chromium/spdy_network_transaction_unittest.cc
@@ -6998,12 +6998,14 @@
 }
 
 #if BUILDFLAG(ENABLE_WEBSOCKETS)
-TEST_F(SpdyNetworkTransactionTest, WebsocketOpensNewConnection) {
+
+TEST_F(SpdyNetworkTransactionTest, WebSocketOpensNewConnection) {
   NormalSpdyTransactionHelper helper(request_, DEFAULT_PRIORITY, log_, nullptr);
   helper.RunPreTestSetup();
 
   // First request opens up an HTTP/2 connection.
-  SpdySerializedFrame req(spdy_util_.ConstructSpdyGet(nullptr, 0, 1, LOWEST));
+  SpdySerializedFrame req(
+      spdy_util_.ConstructSpdyGet(nullptr, 0, 1, DEFAULT_PRIORITY));
   MockWrite writes1[] = {CreateMockWrite(req, 0)};
 
   SpdySerializedFrame resp(spdy_util_.ConstructSpdyGetReply(nullptr, 0, 1));
@@ -7016,7 +7018,7 @@
                             arraysize(writes1));
   helper.AddData(&data1);
 
-  // Websocket request opens a new connection with HTTP/2 disabled.
+  // WebSocket request opens a new connection with HTTP/2 disabled.
   MockWrite writes2[] = {
       MockWrite("GET / HTTP/1.1\r\n"
                 "Host: www.example.org\r\n"
@@ -7096,13 +7098,121 @@
   rv = callback2.WaitForResult();
   ASSERT_THAT(rv, IsOk());
 
-  // HTTP/2 connection is still open, but Websocket request did not pool to it.
+  // HTTP/2 connection is still open, but WebSocket request did not pool to it.
   ASSERT_TRUE(spdy_session);
 
   base::RunLoop().RunUntilIdle();
   data1.Resume();
   helper.VerifyDataConsumed();
 }
+
+TEST_F(SpdyNetworkTransactionTest, WebSocketOverHTTP2) {
+  auto session_deps = std::make_unique<SpdySessionDependencies>();
+  session_deps->enable_websocket_over_http2 = true;
+  NormalSpdyTransactionHelper helper(request_, DEFAULT_PRIORITY, log_,
+                                     std::move(session_deps));
+  helper.RunPreTestSetup();
+
+  SpdySerializedFrame req(
+      spdy_util_.ConstructSpdyGet(nullptr, 0, 1, DEFAULT_PRIORITY));
+  SpdySerializedFrame settings_ack(spdy_util_.ConstructSpdySettingsAck());
+
+  SpdyHeaderBlock websocket_request_headers;
+  websocket_request_headers[kHttp2MethodHeader] = "CONNECT";
+  websocket_request_headers[kHttp2AuthorityHeader] = "www.example.org:443";
+  websocket_request_headers[kHttp2SchemeHeader] = "https";
+  websocket_request_headers[kHttp2PathHeader] = "/";
+  websocket_request_headers[kHttp2ProtocolHeader] = "websocket";
+  websocket_request_headers["origin"] = "http://www.example.org";
+  websocket_request_headers["sec-websocket-version"] = "13";
+  websocket_request_headers["sec-websocket-extensions"] =
+      "permessage-deflate; client_max_window_bits";
+
+  spdy_util_.UpdateWithStreamDestruction(1);
+  SpdySerializedFrame websocket_request(spdy_util_.ConstructSpdyHeaders(
+      3, std::move(websocket_request_headers), DEFAULT_PRIORITY, false));
+
+  MockWrite writes[] = {
+      CreateMockWrite(req, 0), CreateMockWrite(settings_ack, 2),
+      CreateMockWrite(websocket_request, 5),
+  };
+
+  SettingsMap settings;
+  settings[SETTINGS_ENABLE_CONNECT_PROTOCOL] = 1;
+  SpdySerializedFrame settings_frame(
+      spdy_util_.ConstructSpdySettings(settings));
+  SpdySerializedFrame resp1(spdy_util_.ConstructSpdyGetReply(nullptr, 0, 1));
+  SpdySerializedFrame body1(spdy_util_.ConstructSpdyDataFrame(1, true));
+  SpdySerializedFrame websocket_response(
+      spdy_util_.ConstructSpdyGetReply(nullptr, 0, 3));
+  MockRead reads[] = {
+      CreateMockRead(settings_frame, 1),
+      CreateMockRead(resp1, 3),
+      CreateMockRead(body1, 4),
+      CreateMockRead(websocket_response, 6),
+      MockRead(ASYNC, 0, 7),
+  };
+
+  SequencedSocketData data(reads, arraysize(reads), writes, arraysize(writes));
+  helper.AddData(&data);
+
+  TestCompletionCallback callback1;
+  HttpNetworkTransaction trans1(DEFAULT_PRIORITY, helper.session());
+  int rv = trans1.Start(&request_, callback1.callback(), log_);
+  ASSERT_THAT(rv, IsError(ERR_IO_PENDING));
+  rv = callback1.WaitForResult();
+  ASSERT_THAT(rv, IsOk());
+
+  const HttpResponseInfo* response = trans1.GetResponseInfo();
+  ASSERT_TRUE(response->headers);
+  EXPECT_TRUE(response->was_fetched_via_spdy);
+  EXPECT_EQ("HTTP/1.1 200", response->headers->GetStatusLine());
+
+  std::string response_data;
+  rv = ReadTransaction(&trans1, &response_data);
+  EXPECT_THAT(rv, IsOk());
+  EXPECT_EQ("hello!", response_data);
+
+  SpdySessionKey key(HostPortPair::FromURL(request_.url), ProxyServer::Direct(),
+                     PRIVACY_MODE_DISABLED, SocketTag());
+  base::WeakPtr<SpdySession> spdy_session =
+      helper.session()->spdy_session_pool()->FindAvailableSession(
+          key, /* enable_ip_based_pooling = */ true,
+          /* is_websocket = */ true, log_);
+  ASSERT_TRUE(spdy_session);
+  EXPECT_TRUE(spdy_session->support_websocket());
+
+  HttpRequestInfo request2;
+  request2.method = "GET";
+  request2.url = GURL("wss://www.example.org/");
+  request2.traffic_annotation =
+      net::MutableNetworkTrafficAnnotationTag(TRAFFIC_ANNOTATION_FOR_TESTS);
+  EXPECT_TRUE(HostPortPair::FromURL(request_.url)
+                  .Equals(HostPortPair::FromURL(request2.url)));
+  request2.extra_headers.SetHeader("Origin", "http://www.example.org");
+  request2.extra_headers.SetHeader("Sec-WebSocket-Version", "13");
+  // The following two headers must be removed by WebSocketHttp2HandshakeStream.
+  request2.extra_headers.SetHeader("Connection", "Upgrade");
+  request2.extra_headers.SetHeader("Upgrade", "websocket");
+
+  TestWebSocketHandshakeStreamCreateHelper websocket_stream_create_helper;
+
+  HttpNetworkTransaction trans2(DEFAULT_PRIORITY, helper.session());
+  trans2.SetWebSocketHandshakeStreamCreateHelper(
+      &websocket_stream_create_helper);
+
+  TestCompletionCallback callback2;
+  rv = trans2.Start(&request2, callback2.callback(), log_);
+  ASSERT_THAT(rv, IsError(ERR_IO_PENDING));
+  rv = callback2.WaitForResult();
+  ASSERT_THAT(rv, IsOk());
+
+  ASSERT_TRUE(spdy_session);
+
+  base::RunLoop().RunUntilIdle();
+  helper.VerifyDataConsumed();
+}
+
 #endif  // BUILDFLAG(ENABLE_WEBSOCKETS)
 
 }  // namespace net
diff --git a/net/url_request/url_request_ftp_job_unittest.cc b/net/url_request/url_request_ftp_job_unittest.cc
index 8523ef43..2bfbec17 100644
--- a/net/url_request/url_request_ftp_job_unittest.cc
+++ b/net/url_request/url_request_ftp_job_unittest.cc
@@ -130,11 +130,6 @@
     return CONFIG_VALID;
   }
 
-  void IncrementConfigId() {
-    config_.set_id(config_.id() + 1);
-    observer_->OnProxyConfigChanged(config_, ProxyConfigService::CONFIG_VALID);
-  }
-
  private:
   ProxyConfig config_;
   Observer* observer_;
diff --git a/net/url_request/url_request_unittest.cc b/net/url_request/url_request_unittest.cc
index 37f14a97..f1ffbe0 100644
--- a/net/url_request/url_request_unittest.cc
+++ b/net/url_request/url_request_unittest.cc
@@ -89,7 +89,6 @@
 #include "net/log/test_net_log_entry.h"
 #include "net/log/test_net_log_util.h"
 #include "net/net_features.h"
-#include "net/nqe/external_estimate_provider.h"
 #include "net/proxy_resolution/proxy_service.h"
 #include "net/quic/chromium/mock_crypto_client_stream_factory.h"
 #include "net/quic/chromium/quic_server_info.h"
diff --git a/net/websockets/websocket_basic_stream_adapters_test.cc b/net/websockets/websocket_basic_stream_adapters_test.cc
index c018675..9e6f764 100644
--- a/net/websockets/websocket_basic_stream_adapters_test.cc
+++ b/net/websockets/websocket_basic_stream_adapters_test.cc
@@ -34,6 +34,7 @@
 #include "net/test/gtest_util.h"
 #include "net/test/test_data_directory.h"
 #include "net/traffic_annotation/network_traffic_annotation_test_helper.h"
+#include "net/websockets/websocket_test_util.h"
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
@@ -303,23 +304,12 @@
   ~WebSocketSpdyStreamAdapterTest() override = default;
 
   static SpdyHeaderBlock RequestHeaders() {
-    SpdyHeaderBlock request_headers;
-    request_headers[kHttp2MethodHeader] = "CONNECT";
-    request_headers[kHttp2AuthorityHeader] = "www.example.org:443";
-    request_headers[kHttp2SchemeHeader] = "https";
-    request_headers[kHttp2PathHeader] = "/";
-    request_headers[":protocol"] = "websocket";
-    request_headers["origin"] = "http://www.example.org";
-    request_headers["sec-websocket-version"] = "13";
-    request_headers["sec-websocket-extensions"] =
-        "permessage-deflate; client_max_window_bits";
-    return request_headers;
+    return WebSocketHttp2Request("/", "www.example.org:443",
+                                 "http://www.example.org", {});
   }
 
   static SpdyHeaderBlock ResponseHeaders() {
-    SpdyHeaderBlock response_headers;
-    response_headers[kHttp2StatusHeader] = "200";
-    return response_headers;
+    return WebSocketHttp2Response({});
   }
 
   void AddSocketData(SocketDataProvider* data) {
@@ -349,7 +339,7 @@
 
  private:
   const GURL url_;
-  SpdySessionKey key_;
+  const SpdySessionKey key_;
   SpdySessionDependencies session_deps_;
   std::unique_ptr<HttpNetworkSession> session_;
   SSLSocketDataProvider ssl_;
diff --git a/net/websockets/websocket_handshake_constants.cc b/net/websockets/websocket_handshake_constants.cc
index cb03efb33..ce9bb62 100644
--- a/net/websockets/websocket_handshake_constants.cc
+++ b/net/websockets/websocket_handshake_constants.cc
@@ -24,9 +24,6 @@
 
 const char kSecWebSocketProtocolLowercase[] = "sec-websocket-protocol";
 const char kSecWebSocketExtensionsLowercase[] = "sec-websocket-extensions";
-const char kSecWebSocketKeyLowercase[] = "sec-websocket-key";
-const char kSecWebSocketVersionLowercase[] = "sec-websocket-version";
-const char kUpgradeLowercase[] = "upgrade";
 const char kWebSocketLowercase[] = "websocket";
 
 }  // namespace websockets
diff --git a/net/websockets/websocket_handshake_constants.h b/net/websockets/websocket_handshake_constants.h
index f32b5f26..8fb5fc5 100644
--- a/net/websockets/websocket_handshake_constants.h
+++ b/net/websockets/websocket_handshake_constants.h
@@ -64,15 +64,6 @@
 // "sec-websocket-extensions"
 extern const char kSecWebSocketExtensionsLowercase[];
 
-// "sec-webSocket-key"
-extern const char kSecWebSocketKeyLowercase[];
-
-// "sec-websocket-version"
-extern const char kSecWebSocketVersionLowercase[];
-
-// "upgrade"
-extern const char kUpgradeLowercase[];
-
 // "websocket", as used in the "Upgrade:" header. This is always lowercase
 // (except in obsolete versions of the protocol).
 extern const char kWebSocketLowercase[];
diff --git a/net/websockets/websocket_handshake_stream_base.h b/net/websockets/websocket_handshake_stream_base.h
index 9ca9679..feb12a38 100644
--- a/net/websockets/websocket_handshake_stream_base.h
+++ b/net/websockets/websocket_handshake_stream_base.h
@@ -23,6 +23,7 @@
 namespace net {
 
 class ClientSocketHandle;
+class SpdySession;
 
 // WebSocketHandshakeStreamBase is the base class of
 // WebSocketBasicHandshakeStream.  net/http code uses this interface to handle
@@ -49,6 +50,12 @@
     virtual std::unique_ptr<WebSocketHandshakeStreamBase> CreateBasicStream(
         std::unique_ptr<ClientSocketHandle> connection,
         bool using_proxy) = 0;
+
+    // Create a WebSocketHttp2HandshakeStream. This is called after the
+    // underlying HTTP/2 connection has been established but before the stream
+    // has been opened.  This cannot be called more than once.
+    virtual std::unique_ptr<WebSocketHandshakeStreamBase> CreateHttp2Stream(
+        base::WeakPtr<SpdySession> session) = 0;
   };
 
   // This has to have an inline implementation so that the net/url_request/
diff --git a/net/websockets/websocket_handshake_stream_create_helper.cc b/net/websockets/websocket_handshake_stream_create_helper.cc
index a1475381..66168942 100644
--- a/net/websockets/websocket_handshake_stream_create_helper.cc
+++ b/net/websockets/websocket_handshake_stream_create_helper.cc
@@ -11,6 +11,7 @@
 #include "base/memory/weak_ptr.h"
 #include "net/socket/client_socket_handle.h"
 #include "net/websockets/websocket_basic_handshake_stream.h"
+#include "net/websockets/websocket_http2_handshake_stream.h"
 
 namespace net {
 
@@ -45,6 +46,21 @@
   return std::move(stream);
 }
 
+std::unique_ptr<WebSocketHandshakeStreamBase>
+WebSocketHandshakeStreamCreateHelper::CreateHttp2Stream(
+    base::WeakPtr<SpdySession> session) {
+  DCHECK(request_) << "set_request() must be called";
+
+  std::vector<std::string> extensions(
+      1, "permessage-deflate; client_max_window_bits");
+  std::unique_ptr<WebSocketHandshakeStreamBase> stream =
+      std::make_unique<WebSocketHttp2HandshakeStream>(
+          session, connect_delegate_, requested_subprotocols_, extensions,
+          request_);
+  request_->OnHandshakeStreamCreated(stream.get());
+  return stream;
+}
+
 void WebSocketHandshakeStreamCreateHelper::OnBasicStreamCreated(
     WebSocketBasicHandshakeStream* stream) {}
 
diff --git a/net/websockets/websocket_handshake_stream_create_helper.h b/net/websockets/websocket_handshake_stream_create_helper.h
index 2622de0..c21d351 100644
--- a/net/websockets/websocket_handshake_stream_create_helper.h
+++ b/net/websockets/websocket_handshake_stream_create_helper.h
@@ -17,6 +17,7 @@
 namespace net {
 
 class WebSocketStreamRequest;
+class SpdySession;
 class WebSocketBasicHandshakeStream;
 
 // Implementation of WebSocketHandshakeStreamBase::CreateHelper. This class is
@@ -37,11 +38,15 @@
 
   // WebSocketHandshakeStreamBase::CreateHelper methods
 
-  // Creates a WebSocketBasicHandshakeStream.
+  // Creates a WebSocketBasicHandshakeStream over a TCP/IP or TLS socket.
   std::unique_ptr<WebSocketHandshakeStreamBase> CreateBasicStream(
       std::unique_ptr<ClientSocketHandle> connection,
       bool using_proxy) override;
 
+  // Creates a WebSocketHttp2HandshakeStream over an HTTP/2 connection.
+  std::unique_ptr<WebSocketHandshakeStreamBase> CreateHttp2Stream(
+      base::WeakPtr<SpdySession> session) override;
+
   // WebSocketHandshakeStreamCreateHelper methods
 
   // This method must be called before CreateBasicStream().
diff --git a/net/websockets/websocket_handshake_stream_create_helper_test.cc b/net/websockets/websocket_handshake_stream_create_helper_test.cc
index 614170ef..7c45da2 100644
--- a/net/websockets/websocket_handshake_stream_create_helper_test.cc
+++ b/net/websockets/websocket_handshake_stream_create_helper_test.cc
@@ -12,6 +12,8 @@
 #include "net/base/completion_callback.h"
 #include "net/base/completion_once_callback.h"
 #include "net/base/net_errors.h"
+#include "net/base/proxy_server.h"
+#include "net/http/http_network_session.h"
 #include "net/http/http_request_headers.h"
 #include "net/http/http_request_info.h"
 #include "net/http/http_response_headers.h"
@@ -20,7 +22,14 @@
 #include "net/socket/client_socket_handle.h"
 #include "net/socket/socket_tag.h"
 #include "net/socket/socket_test_util.h"
+#include "net/spdy/chromium/spdy_session.h"
+#include "net/spdy/chromium/spdy_session_key.h"
+#include "net/spdy/chromium/spdy_test_util_common.h"
+#include "net/ssl/ssl_config.h"
+#include "net/ssl/ssl_info.h"
+#include "net/test/cert_test_util.h"
 #include "net/test/gtest_util.h"
+#include "net/test/test_data_directory.h"
 #include "net/traffic_annotation/network_traffic_annotation.h"
 #include "net/websockets/websocket_basic_handshake_stream.h"
 #include "net/websockets/websocket_stream.h"
@@ -30,13 +39,29 @@
 #include "url/gurl.h"
 #include "url/origin.h"
 
-using net::test::IsOk;
-
+using ::net::test::IsError;
+using ::net::test::IsOk;
+using ::testing::StrictMock;
+using ::testing::TestWithParam;
+using ::testing::Values;
 using ::testing::_;
 
 namespace net {
 namespace {
 
+enum HandshakeStreamType { BASIC_HANDSHAKE_STREAM, HTTP2_HANDSHAKE_STREAM };
+
+std::string WebSocketExtraHeadersToString(WebSocketExtraHeaders headers) {
+  std::string answer;
+  for (const auto& header : headers) {
+    answer.append(header.first);
+    answer.append(": ");
+    answer.append(header.second);
+    answer.append("\r\n");
+  }
+  return answer;
+}
+
 // This class encapsulates the details of creating a mock ClientSocketHandle.
 class MockClientSocketHandleFactory {
  public:
@@ -50,7 +75,7 @@
       const std::string& expect_written,
       const std::string& return_to_read) {
     socket_factory_maker_.SetExpectations(expect_written, return_to_read);
-    std::unique_ptr<ClientSocketHandle> socket_handle(new ClientSocketHandle);
+    auto socket_handle = std::make_unique<ClientSocketHandle>();
     socket_handle->Init("a", scoped_refptr<MockTransportSocketParams>(), MEDIUM,
                         SocketTag(), ClientSocketPool::RespectLimits::ENABLED,
                         CompletionCallback(), &pool_, NetLogWithSource());
@@ -84,55 +109,41 @@
 
 class MockWebSocketStreamRequest : public WebSocketStreamRequest {
  public:
+  ~MockWebSocketStreamRequest() override = default;
+
   MOCK_METHOD1(OnHandshakeStreamCreated,
                void(WebSocketHandshakeStreamBase* handshake_stream));
   MOCK_METHOD1(OnFailure, void(const std::string& message));
 };
 
-class WebSocketHandshakeStreamCreateHelperTest : public ::testing::Test {
+class WebSocketHandshakeStreamCreateHelperTest
+    : public TestWithParam<HandshakeStreamType> {
  protected:
   std::unique_ptr<WebSocketStream> CreateAndInitializeStream(
       const std::vector<std::string>& sub_protocols,
-      const std::string& extra_request_headers,
-      const std::string& extra_response_headers) {
-    static const char kOrigin[] = "http://localhost";
+      const WebSocketExtraHeaders& extra_request_headers,
+      const WebSocketExtraHeaders& extra_response_headers) {
+    const char kPath[] = "/";
+    const char kOrigin[] = "http://origin.example.org";
+    const GURL url("wss://www.example.org/");
+    NetLogWithSource net_log;
+
     WebSocketHandshakeStreamCreateHelper create_helper(&connect_delegate_,
                                                        sub_protocols);
+    create_helper.set_stream_request(&stream_request_);
 
-    EXPECT_CALL(stream_request, OnHandshakeStreamCreated(_)).Times(1);
-    EXPECT_CALL(stream_request, OnFailure(_)).Times(0);
-
-    create_helper.set_stream_request(&stream_request);
-
-    std::unique_ptr<ClientSocketHandle> socket_handle =
-        socket_handle_factory_.CreateClientSocketHandle(
-            WebSocketStandardRequest("/", "localhost",
-                                     url::Origin::Create(GURL(kOrigin)), "",
-                                     extra_request_headers),
-            WebSocketStandardResponse(extra_response_headers));
-
-    std::unique_ptr<WebSocketHandshakeStreamBase> handshake =
-        create_helper.CreateBasicStream(std::move(socket_handle), false);
-
-    // If in future the implementation type returned by CreateBasicStream()
-    // changes, this static_cast will be wrong. However, in that case the test
-    // will fail and AddressSanitizer should identify the issue.
-    static_cast<WebSocketBasicHandshakeStream*>(handshake.get())
-        ->SetWebSocketKeyForTesting("dGhlIHNhbXBsZSBub25jZQ==");
+    EXPECT_CALL(stream_request_, OnHandshakeStreamCreated(_)).Times(1);
+    EXPECT_CALL(stream_request_, OnFailure(_)).Times(0);
 
     HttpRequestInfo request_info;
-    request_info.url = GURL("ws://localhost/");
+    request_info.url = url;
     request_info.method = "GET";
     request_info.load_flags = LOAD_DISABLE_CACHE;
     request_info.traffic_annotation =
         MutableNetworkTrafficAnnotationTag(TRAFFIC_ANNOTATION_FOR_TESTS);
-    int rv = handshake->InitializeStream(&request_info, true, DEFAULT_PRIORITY,
-                                         NetLogWithSource(),
-                                         CompletionOnceCallback());
-    EXPECT_THAT(rv, IsOk());
 
     HttpRequestHeaders headers;
-    headers.SetHeader("Host", "localhost");
+    headers.SetHeader("Host", "www.example.org");
     headers.SetHeader("Connection", "Upgrade");
     headers.SetHeader("Pragma", "no-cache");
     headers.SetHeader("Cache-Control", "no-cache");
@@ -143,62 +154,155 @@
     headers.SetHeader("Accept-Encoding", "gzip, deflate");
     headers.SetHeader("Accept-Language", "en-us,fr");
 
-    HttpResponseInfo response;
-    TestCompletionCallback dummy;
+    switch (GetParam()) {
+      case BASIC_HANDSHAKE_STREAM: {
+        std::unique_ptr<ClientSocketHandle> socket_handle =
+            socket_handle_factory_.CreateClientSocketHandle(
+                WebSocketStandardRequest(
+                    kPath, "www.example.org",
+                    url::Origin::Create(GURL(kOrigin)), "",
+                    WebSocketExtraHeadersToString(extra_request_headers)),
+                WebSocketStandardResponse(
+                    WebSocketExtraHeadersToString(extra_response_headers)));
 
-    rv = handshake->SendRequest(headers, &response, dummy.callback());
+        std::unique_ptr<WebSocketHandshakeStreamBase> handshake =
+            create_helper.CreateBasicStream(std::move(socket_handle), false);
 
-    EXPECT_THAT(rv, IsOk());
+        // If in future the implementation type returned by CreateBasicStream()
+        // changes, this static_cast will be wrong. However, in that case the
+        // test will fail and AddressSanitizer should identify the issue.
+        static_cast<WebSocketBasicHandshakeStream*>(handshake.get())
+            ->SetWebSocketKeyForTesting("dGhlIHNhbXBsZSBub25jZQ==");
 
-    rv = handshake->ReadResponseHeaders(dummy.callback());
-    EXPECT_THAT(rv, IsOk());
-    EXPECT_EQ(101, response.headers->response_code());
-    EXPECT_TRUE(response.headers->HasHeaderValue("Connection", "Upgrade"));
-    EXPECT_TRUE(response.headers->HasHeaderValue("Upgrade", "websocket"));
-    return handshake->Upgrade();
+        int rv =
+            handshake->InitializeStream(&request_info, true, DEFAULT_PRIORITY,
+                                        net_log, CompletionOnceCallback());
+        EXPECT_THAT(rv, IsOk());
+
+        HttpResponseInfo response;
+        TestCompletionCallback request_callback;
+        rv = handshake->SendRequest(headers, &response,
+                                    request_callback.callback());
+        EXPECT_THAT(rv, IsOk());
+
+        TestCompletionCallback response_callback;
+        rv = handshake->ReadResponseHeaders(response_callback.callback());
+        EXPECT_THAT(rv, IsOk());
+        EXPECT_EQ(101, response.headers->response_code());
+        EXPECT_TRUE(response.headers->HasHeaderValue("Connection", "Upgrade"));
+        EXPECT_TRUE(response.headers->HasHeaderValue("Upgrade", "websocket"));
+        return handshake->Upgrade();
+      }
+      case HTTP2_HANDSHAKE_STREAM: {
+        SpdyTestUtil spdy_util;
+        SpdyHeaderBlock request_header_block = WebSocketHttp2Request(
+            kPath, "www.example.org:443", kOrigin, extra_request_headers);
+        SpdySerializedFrame request_headers(spdy_util.ConstructSpdyHeaders(
+            1, std::move(request_header_block), DEFAULT_PRIORITY, false));
+        MockWrite writes[] = {CreateMockWrite(request_headers, 0)};
+
+        SpdyHeaderBlock response_header_block =
+            WebSocketHttp2Response(extra_response_headers);
+        SpdySerializedFrame response_headers(
+            spdy_util.ConstructSpdyResponseHeaders(
+                1, std::move(response_header_block), false));
+        MockRead reads[] = {CreateMockRead(response_headers, 1),
+                            MockRead(ASYNC, 0, 2)};
+
+        SequencedSocketData data(reads, arraysize(reads), writes,
+                                 arraysize(writes));
+
+        SSLSocketDataProvider ssl(ASYNC, OK);
+        ssl.ssl_info.cert =
+            ImportCertFromFile(GetTestCertsDirectory(), "spdy_pooling.pem");
+
+        SpdySessionDependencies session_deps;
+        session_deps.socket_factory->AddSocketDataProvider(&data);
+        session_deps.socket_factory->AddSSLSocketDataProvider(&ssl);
+
+        std::unique_ptr<HttpNetworkSession> http_network_session =
+            SpdySessionDependencies::SpdyCreateSession(&session_deps);
+        const SpdySessionKey key(HostPortPair::FromURL(url),
+                                 ProxyServer::Direct(), PRIVACY_MODE_DISABLED,
+                                 SocketTag());
+        base::WeakPtr<SpdySession> spdy_session =
+            CreateSpdySession(http_network_session.get(), key, net_log);
+        std::unique_ptr<WebSocketHandshakeStreamBase> handshake =
+            create_helper.CreateHttp2Stream(spdy_session);
+
+        int rv = handshake->InitializeStream(
+            &request_info, true, DEFAULT_PRIORITY, NetLogWithSource(),
+            CompletionOnceCallback());
+        EXPECT_THAT(rv, IsOk());
+
+        HttpResponseInfo response;
+        TestCompletionCallback request_callback;
+        rv = handshake->SendRequest(headers, &response,
+                                    request_callback.callback());
+        EXPECT_THAT(rv, IsError(ERR_IO_PENDING));
+        rv = request_callback.WaitForResult();
+        EXPECT_THAT(rv, IsOk());
+
+        TestCompletionCallback response_callback;
+        rv = handshake->ReadResponseHeaders(response_callback.callback());
+        EXPECT_THAT(rv, IsError(ERR_IO_PENDING));
+        rv = response_callback.WaitForResult();
+        EXPECT_THAT(rv, IsOk());
+
+        EXPECT_EQ(200, response.headers->response_code());
+        return handshake->Upgrade();
+      }
+      default:
+        NOTREACHED();
+        return nullptr;
+    }
   }
 
   MockClientSocketHandleFactory socket_handle_factory_;
   TestConnectDelegate connect_delegate_;
-  MockWebSocketStreamRequest stream_request;
+  StrictMock<MockWebSocketStreamRequest> stream_request_;
 };
 
+INSTANTIATE_TEST_CASE_P(,
+                        WebSocketHandshakeStreamCreateHelperTest,
+                        Values(BASIC_HANDSHAKE_STREAM, HTTP2_HANDSHAKE_STREAM));
+
 // Confirm that the basic case works as expected.
-TEST_F(WebSocketHandshakeStreamCreateHelperTest, BasicStream) {
+TEST_P(WebSocketHandshakeStreamCreateHelperTest, BasicStream) {
   std::unique_ptr<WebSocketStream> stream =
-      CreateAndInitializeStream(std::vector<std::string>(), "", "");
+      CreateAndInitializeStream({}, {}, {});
   EXPECT_EQ("", stream->GetExtensions());
   EXPECT_EQ("", stream->GetSubProtocol());
 }
 
 // Verify that the sub-protocols are passed through.
-TEST_F(WebSocketHandshakeStreamCreateHelperTest, SubProtocols) {
+TEST_P(WebSocketHandshakeStreamCreateHelperTest, SubProtocols) {
   std::vector<std::string> sub_protocols;
   sub_protocols.push_back("chat");
   sub_protocols.push_back("superchat");
   std::unique_ptr<WebSocketStream> stream = CreateAndInitializeStream(
-      sub_protocols, "Sec-WebSocket-Protocol: chat, superchat\r\n",
-      "Sec-WebSocket-Protocol: superchat\r\n");
+      sub_protocols, {{"Sec-WebSocket-Protocol", "chat, superchat"}},
+      {{"Sec-WebSocket-Protocol", "superchat"}});
   EXPECT_EQ("superchat", stream->GetSubProtocol());
 }
 
 // Verify that extension name is available. Bad extension names are tested in
 // websocket_stream_test.cc.
-TEST_F(WebSocketHandshakeStreamCreateHelperTest, Extensions) {
+TEST_P(WebSocketHandshakeStreamCreateHelperTest, Extensions) {
   std::unique_ptr<WebSocketStream> stream = CreateAndInitializeStream(
-      std::vector<std::string>(), "",
-      "Sec-WebSocket-Extensions: permessage-deflate\r\n");
+      {}, {}, {{"Sec-WebSocket-Extensions", "permessage-deflate"}});
   EXPECT_EQ("permessage-deflate", stream->GetExtensions());
 }
 
 // Verify that extension parameters are available. Bad parameters are tested in
 // websocket_stream_test.cc.
-TEST_F(WebSocketHandshakeStreamCreateHelperTest, ExtensionParameters) {
+TEST_P(WebSocketHandshakeStreamCreateHelperTest, ExtensionParameters) {
   std::unique_ptr<WebSocketStream> stream = CreateAndInitializeStream(
-      std::vector<std::string>(), "",
-      "Sec-WebSocket-Extensions: permessage-deflate;"
-      " client_max_window_bits=14; server_max_window_bits=14;"
-      " server_no_context_takeover; client_no_context_takeover\r\n");
+      {}, {},
+      {{"Sec-WebSocket-Extensions",
+        "permessage-deflate;"
+        " client_max_window_bits=14; server_max_window_bits=14;"
+        " server_no_context_takeover; client_no_context_takeover"}});
 
   EXPECT_EQ(
       "permessage-deflate;"
diff --git a/net/websockets/websocket_http2_handshake_stream.cc b/net/websockets/websocket_http2_handshake_stream.cc
new file mode 100644
index 0000000..782a8a43
--- /dev/null
+++ b/net/websockets/websocket_http2_handshake_stream.cc
@@ -0,0 +1,503 @@
+// 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 "net/websockets/websocket_http2_handshake_stream.h"
+
+#include <cstddef>
+#include <unordered_set>
+#include <utility>
+
+#include "base/bind.h"
+#include "base/logging.h"
+#include "base/metrics/histogram_macros.h"
+#include "base/strings/string_util.h"
+#include "base/strings/stringprintf.h"
+#include "base/time/time.h"
+#include "net/http/http_request_headers.h"
+#include "net/http/http_request_info.h"
+#include "net/http/http_response_headers.h"
+#include "net/http/http_status_code.h"
+#include "net/spdy/chromium/spdy_http_utils.h"
+#include "net/spdy/chromium/spdy_session.h"
+#include "net/traffic_annotation/network_traffic_annotation.h"
+#include "net/websockets/websocket_basic_stream.h"
+#include "net/websockets/websocket_deflate_parameters.h"
+#include "net/websockets/websocket_deflate_predictor_impl.h"
+#include "net/websockets/websocket_deflate_stream.h"
+#include "net/websockets/websocket_deflater.h"
+#include "net/websockets/websocket_extension_parser.h"
+#include "net/websockets/websocket_handshake_constants.h"
+#include "net/websockets/websocket_handshake_request_info.h"
+
+namespace net {
+
+// TODO(ricea): If more extensions are added, replace this with a more general
+// mechanism.
+struct WebSocketExtensionParams {
+  bool deflate_enabled = false;
+  WebSocketDeflateParameters deflate_parameters;
+};
+
+namespace {
+
+void AddVectorHeaderIfNonEmpty(const char* name,
+                               const std::vector<std::string>& value,
+                               HttpRequestHeaders* headers) {
+  if (value.empty())
+    return;
+  headers->SetHeader(name, base::JoinString(value, ", "));
+}
+
+std::string MultipleHeaderValuesMessage(const std::string& header_name) {
+  return std::string("'") + header_name +
+         "' header must not appear more than once in a response";
+}
+
+bool ValidateStatus(const HttpResponseHeaders* headers) {
+  return headers->GetStatusLine() == "HTTP/1.1 200";
+}
+
+bool ValidateSubProtocol(
+    const HttpResponseHeaders* headers,
+    const std::vector<std::string>& requested_sub_protocols,
+    std::string* sub_protocol,
+    std::string* failure_message) {
+  size_t iter = 0;
+  std::string value;
+  std::unordered_set<std::string> requested_set(requested_sub_protocols.begin(),
+                                                requested_sub_protocols.end());
+  int count = 0;
+  bool has_multiple_protocols = false;
+  bool has_invalid_protocol = false;
+
+  while (!has_invalid_protocol || !has_multiple_protocols) {
+    std::string temp_value;
+    if (!headers->EnumerateHeader(
+            &iter, websockets::kSecWebSocketProtocolLowercase, &temp_value))
+      break;
+    value = temp_value;
+    if (requested_set.count(value) == 0)
+      has_invalid_protocol = true;
+    if (++count > 1)
+      has_multiple_protocols = true;
+  }
+
+  if (has_multiple_protocols) {
+    *failure_message =
+        MultipleHeaderValuesMessage(websockets::kSecWebSocketProtocolLowercase);
+    return false;
+  } else if (count > 0 && requested_sub_protocols.size() == 0) {
+    *failure_message = std::string(
+                           "Response must not include 'Sec-WebSocket-Protocol' "
+                           "header if not present in request: ") +
+                       value;
+    return false;
+  } else if (has_invalid_protocol) {
+    *failure_message = "'Sec-WebSocket-Protocol' header value '" + value +
+                       "' in response does not match any of sent values";
+    return false;
+  } else if (requested_sub_protocols.size() > 0 && count == 0) {
+    *failure_message =
+        "Sent non-empty 'Sec-WebSocket-Protocol' header "
+        "but no response was received";
+    return false;
+  }
+  *sub_protocol = value;
+  return true;
+}
+
+bool ValidateExtensions(const HttpResponseHeaders* headers,
+                        std::string* accepted_extensions_descriptor,
+                        std::string* failure_message,
+                        WebSocketExtensionParams* params) {
+  size_t iter = 0;
+  std::string header_value;
+  std::vector<std::string> header_values;
+  // TODO(ricea): If adding support for additional extensions, generalise this
+  // code.
+  bool seen_permessage_deflate = false;
+  while (headers->EnumerateHeader(
+      &iter, websockets::kSecWebSocketExtensionsLowercase, &header_value)) {
+    WebSocketExtensionParser parser;
+    if (!parser.Parse(header_value)) {
+      // TODO(yhirano) Set appropriate failure message.
+      *failure_message =
+          "'Sec-WebSocket-Extensions' header value is "
+          "rejected by the parser: " +
+          header_value;
+      return false;
+    }
+
+    const std::vector<WebSocketExtension>& extensions = parser.extensions();
+    for (const auto& extension : extensions) {
+      if (extension.name() == "permessage-deflate") {
+        if (seen_permessage_deflate) {
+          *failure_message = "Received duplicate permessage-deflate response";
+          return false;
+        }
+        seen_permessage_deflate = true;
+        auto& deflate_parameters = params->deflate_parameters;
+        if (!deflate_parameters.Initialize(extension, failure_message) ||
+            !deflate_parameters.IsValidAsResponse(failure_message)) {
+          *failure_message = "Error in permessage-deflate: " + *failure_message;
+          return false;
+        }
+        // Note that we don't have to check the request-response compatibility
+        // here because we send a request compatible with any valid responses.
+        // TODO(yhirano): Place a DCHECK here.
+
+        header_values.push_back(header_value);
+      } else {
+        *failure_message = "Found an unsupported extension '" +
+                           extension.name() +
+                           "' in 'Sec-WebSocket-Extensions' header";
+        return false;
+      }
+    }
+  }
+  *accepted_extensions_descriptor = base::JoinString(header_values, ", ");
+  params->deflate_enabled = seen_permessage_deflate;
+  return true;
+}
+
+}  // namespace
+
+WebSocketHttp2HandshakeStream::WebSocketHttp2HandshakeStream(
+    base::WeakPtr<SpdySession> session,
+    WebSocketStream::ConnectDelegate* connect_delegate,
+    std::vector<std::string> requested_sub_protocols,
+    std::vector<std::string> requested_extensions,
+    WebSocketStreamRequest* request)
+    : session_(session),
+      connect_delegate_(connect_delegate),
+      http_response_info_(nullptr),
+      requested_sub_protocols_(requested_sub_protocols),
+      requested_extensions_(requested_extensions),
+      stream_request_(request),
+      request_info_(nullptr),
+      stream_closed_(false),
+      stream_error_(OK),
+      response_headers_complete_(false) {
+  DCHECK(connect_delegate);
+  DCHECK(request);
+}
+
+WebSocketHttp2HandshakeStream::~WebSocketHttp2HandshakeStream() {
+  spdy_stream_request_.reset();
+}
+
+int WebSocketHttp2HandshakeStream::InitializeStream(
+    const HttpRequestInfo* request_info,
+    bool can_send_early,
+    RequestPriority priority,
+    const NetLogWithSource& net_log,
+    CompletionOnceCallback callback) {
+  request_info_ = request_info;
+  priority_ = priority;
+  net_log_ = net_log;
+  return OK;
+}
+
+int WebSocketHttp2HandshakeStream::SendRequest(
+    const HttpRequestHeaders& headers,
+    HttpResponseInfo* response,
+    CompletionOnceCallback callback) {
+  DCHECK(!headers.HasHeader(websockets::kSecWebSocketKey));
+  DCHECK(!headers.HasHeader(websockets::kSecWebSocketProtocol));
+  DCHECK(!headers.HasHeader(websockets::kSecWebSocketExtensions));
+  DCHECK(headers.HasHeader(HttpRequestHeaders::kOrigin));
+  DCHECK(headers.HasHeader(websockets::kUpgrade));
+  DCHECK(headers.HasHeader(HttpRequestHeaders::kConnection));
+  DCHECK(headers.HasHeader(websockets::kSecWebSocketVersion));
+
+  if (!session_) {
+    OnFailure("Connection closed before sending request.");
+    return ERR_CONNECTION_CLOSED;
+  }
+
+  http_response_info_ = response;
+
+  IPEndPoint address;
+  int result = session_->GetPeerAddress(&address);
+  if (result != OK) {
+    OnFailure("Error getting IP address.");
+    return result;
+  }
+  http_response_info_->socket_address = HostPortPair::FromIPEndPoint(address);
+
+  auto request = std::make_unique<WebSocketHandshakeRequestInfo>(
+      request_info_->url, base::Time::Now());
+  request->headers.CopyFrom(headers);
+
+  AddVectorHeaderIfNonEmpty(websockets::kSecWebSocketExtensions,
+                            requested_extensions_, &request->headers);
+  AddVectorHeaderIfNonEmpty(websockets::kSecWebSocketProtocol,
+                            requested_sub_protocols_, &request->headers);
+
+  CreateSpdyHeadersFromHttpRequestForWebSocket(
+      request_info_->url, request->headers, &http2_request_headers_);
+
+  connect_delegate_->OnStartOpeningHandshake(std::move(request));
+
+  callback_ = std::move(callback);
+  spdy_stream_request_ = std::make_unique<SpdyStreamRequest>();
+  int rv = spdy_stream_request_->StartRequest(
+      SPDY_BIDIRECTIONAL_STREAM, session_, request_info_->url, priority_,
+      net_log_,
+      base::BindOnce(&WebSocketHttp2HandshakeStream::StartRequestCallback,
+                     base::Unretained(this)));
+  if (rv == OK) {
+    StartRequestCallback(rv);
+    return ERR_IO_PENDING;
+  }
+  return rv;
+}
+
+int WebSocketHttp2HandshakeStream::ReadResponseHeaders(
+    CompletionOnceCallback callback) {
+  if (stream_closed_)
+    return stream_error_;
+
+  if (response_headers_complete_)
+    return ValidateResponse();
+
+  callback_ = std::move(callback);
+  return ERR_IO_PENDING;
+}
+
+int WebSocketHttp2HandshakeStream::ReadResponseBody(
+    IOBuffer* buf,
+    int buf_len,
+    CompletionOnceCallback callback) {
+  // Callers should instead call Upgrade() to get a WebSocketStream
+  // and call ReadFrames() on that.
+  NOTREACHED();
+  return OK;
+}
+
+void WebSocketHttp2HandshakeStream::Close(bool not_reusable) {
+  spdy_stream_request_.reset();
+  if (stream_) {
+    stream_ = nullptr;
+    stream_closed_ = true;
+    stream_error_ = ERR_CONNECTION_CLOSED;
+  }
+  stream_adapter_.reset();
+}
+
+bool WebSocketHttp2HandshakeStream::IsResponseBodyComplete() const {
+  return false;
+}
+
+bool WebSocketHttp2HandshakeStream::IsConnectionReused() const {
+  return true;
+}
+
+void WebSocketHttp2HandshakeStream::SetConnectionReused() {}
+
+bool WebSocketHttp2HandshakeStream::CanReuseConnection() const {
+  return false;
+}
+
+int64_t WebSocketHttp2HandshakeStream::GetTotalReceivedBytes() const {
+  return stream_ ? stream_->raw_received_bytes() : 0;
+}
+
+int64_t WebSocketHttp2HandshakeStream::GetTotalSentBytes() const {
+  return stream_ ? stream_->raw_sent_bytes() : 0;
+}
+
+bool WebSocketHttp2HandshakeStream::GetAlternativeService(
+    AlternativeService* alternative_service) const {
+  return false;
+}
+
+bool WebSocketHttp2HandshakeStream::GetLoadTimingInfo(
+    LoadTimingInfo* load_timing_info) const {
+  return stream_ && stream_->GetLoadTimingInfo(load_timing_info);
+}
+
+void WebSocketHttp2HandshakeStream::GetSSLInfo(SSLInfo* ssl_info) {
+  if (stream_)
+    stream_->GetSSLInfo(ssl_info);
+}
+
+void WebSocketHttp2HandshakeStream::GetSSLCertRequestInfo(
+    SSLCertRequestInfo* cert_request_info) {
+  // A multiplexed stream cannot request client certificates. Client
+  // authentication may only occur during the initial SSL handshake.
+  NOTREACHED();
+}
+
+bool WebSocketHttp2HandshakeStream::GetRemoteEndpoint(IPEndPoint* endpoint) {
+  return session_ && session_->GetRemoteEndpoint(endpoint);
+}
+
+void WebSocketHttp2HandshakeStream::PopulateNetErrorDetails(
+    NetErrorDetails* /*details*/) {
+  return;
+}
+
+Error WebSocketHttp2HandshakeStream::GetTokenBindingSignature(
+    crypto::ECPrivateKey* key,
+    TokenBindingType tb_type,
+    std::vector<uint8_t>* out) {
+  NOTREACHED();
+  return ERR_NOT_IMPLEMENTED;
+}
+
+void WebSocketHttp2HandshakeStream::Drain(HttpNetworkSession* session) {
+  Close(true /* not_reusable */);
+}
+
+void WebSocketHttp2HandshakeStream::SetPriority(RequestPriority priority) {
+  priority_ = priority;
+  if (stream_)
+    stream_->set_priority(priority_);
+}
+
+HttpStream* WebSocketHttp2HandshakeStream::RenewStreamForAuth() {
+  // Renewing the stream is not supported.
+  return nullptr;
+}
+
+std::unique_ptr<WebSocketStream> WebSocketHttp2HandshakeStream::Upgrade() {
+  DCHECK(extension_params_.get());
+
+  stream_adapter_->DetachDelegate();
+  std::unique_ptr<WebSocketStream> basic_stream =
+      std::make_unique<WebSocketBasicStream>(
+          std::move(stream_adapter_), nullptr, sub_protocol_, extensions_);
+
+  if (!extension_params_->deflate_enabled)
+    return basic_stream;
+
+  UMA_HISTOGRAM_ENUMERATION(
+      "Net.WebSocket.DeflateMode",
+      extension_params_->deflate_parameters.client_context_take_over_mode(),
+      WebSocketDeflater::NUM_CONTEXT_TAKEOVER_MODE_TYPES);
+
+  return std::make_unique<WebSocketDeflateStream>(
+      std::move(basic_stream), extension_params_->deflate_parameters,
+      std::make_unique<WebSocketDeflatePredictorImpl>());
+}
+
+void WebSocketHttp2HandshakeStream::OnHeadersSent() {
+  base::ResetAndReturn(&callback_).Run(OK);
+}
+
+void WebSocketHttp2HandshakeStream::OnHeadersReceived(
+    const SpdyHeaderBlock& response_headers) {
+  DCHECK(!response_headers_complete_);
+  DCHECK(http_response_info_);
+
+  response_headers_complete_ = true;
+
+  const bool headers_valid =
+      SpdyHeadersToHttpResponse(response_headers, http_response_info_);
+  DCHECK(headers_valid);
+
+  http_response_info_->response_time = stream_->response_time();
+  // Do not store SSLInfo in the response here, HttpNetworkTransaction will take
+  // care of that part.
+  http_response_info_->was_alpn_negotiated = true;
+  http_response_info_->request_time = stream_->GetRequestTime();
+  http_response_info_->connection_info =
+      HttpResponseInfo::CONNECTION_INFO_HTTP2;
+  http_response_info_->alpn_negotiated_protocol =
+      HttpResponseInfo::ConnectionInfoToString(
+          http_response_info_->connection_info);
+  http_response_info_->vary_data.Init(*request_info_,
+                                      *http_response_info_->headers.get());
+
+  if (callback_)
+    base::ResetAndReturn(&callback_).Run(ValidateResponse());
+}
+
+void WebSocketHttp2HandshakeStream::OnClose(int status) {
+  DCHECK(stream_adapter_);
+  DCHECK_GT(ERR_IO_PENDING, status);
+
+  stream_closed_ = true;
+  stream_error_ = status;
+  stream_ = nullptr;
+
+  stream_adapter_.reset();
+
+  OnFailure(std::string("Stream closed with error: ") + ErrorToString(status));
+
+  if (callback_)
+    base::ResetAndReturn(&callback_).Run(status);
+}
+
+void WebSocketHttp2HandshakeStream::StartRequestCallback(int rv) {
+  DCHECK(callback_);
+  if (rv != OK) {
+    spdy_stream_request_.reset();
+    base::ResetAndReturn(&callback_).Run(rv);
+    return;
+  }
+  stream_ = spdy_stream_request_->ReleaseStream();
+  spdy_stream_request_.reset();
+  stream_adapter_ =
+      std::make_unique<WebSocketSpdyStreamAdapter>(stream_, this, net_log_);
+  rv = stream_->SendRequestHeaders(std::move(http2_request_headers_),
+                                   MORE_DATA_TO_SEND);
+  // SendRequestHeaders() always returns asynchronously,
+  // and instead of taking a callback, it calls OnHeadersSent().
+  DCHECK_EQ(ERR_IO_PENDING, rv);
+}
+
+int WebSocketHttp2HandshakeStream::ValidateResponse() {
+  DCHECK(http_response_info_);
+  const HttpResponseHeaders* headers = http_response_info_->headers.get();
+  const int response_code = headers->response_code();
+  switch (response_code) {
+    case HTTP_OK:
+      OnFinishOpeningHandshake();
+      return ValidateUpgradeResponse(headers);
+
+    // We need to pass these through for authentication to work.
+    case HTTP_UNAUTHORIZED:
+    case HTTP_PROXY_AUTHENTICATION_REQUIRED:
+      return OK;
+
+    // Other status codes are potentially risky (see the warnings in the
+    // WHATWG WebSocket API spec) and so are dropped by default.
+    default:
+      OnFailure(base::StringPrintf(
+          "Error during WebSocket handshake: Unexpected response code: %d",
+          headers->response_code()));
+      OnFinishOpeningHandshake();
+      return ERR_INVALID_RESPONSE;
+  }
+}
+
+int WebSocketHttp2HandshakeStream::ValidateUpgradeResponse(
+    const HttpResponseHeaders* headers) {
+  extension_params_ = std::make_unique<WebSocketExtensionParams>();
+  std::string failure_message;
+  if (ValidateStatus(headers) &&
+      ValidateSubProtocol(headers, requested_sub_protocols_, &sub_protocol_,
+                          &failure_message) &&
+      ValidateExtensions(headers, &extensions_, &failure_message,
+                         extension_params_.get())) {
+    return OK;
+  }
+  OnFailure("Error during WebSocket handshake: " + failure_message);
+  return ERR_INVALID_RESPONSE;
+}
+
+void WebSocketHttp2HandshakeStream::OnFinishOpeningHandshake() {
+  DCHECK(http_response_info_);
+  WebSocketDispatchOnFinishOpeningHandshake(
+      connect_delegate_, request_info_->url, http_response_info_->headers,
+      http_response_info_->response_time);
+}
+
+void WebSocketHttp2HandshakeStream::OnFailure(const std::string& message) {
+  stream_request_->OnFailure(message);
+}
+
+}  // namespace net
diff --git a/net/websockets/websocket_http2_handshake_stream.h b/net/websockets/websocket_http2_handshake_stream.h
new file mode 100644
index 0000000..7036e4b6
--- /dev/null
+++ b/net/websockets/websocket_http2_handshake_stream.h
@@ -0,0 +1,190 @@
+// 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 NET_WEBSOCKETS_WEBSOCKET_HTTP2_HANDSHAKE_STREAM_H_
+#define NET_WEBSOCKETS_WEBSOCKET_HTTP2_HANDSHAKE_STREAM_H_
+
+#include <stdint.h>
+
+#include <memory>
+#include <string>
+#include <vector>
+
+#include "base/macros.h"
+#include "base/memory/weak_ptr.h"
+#include "net/base/completion_once_callback.h"
+#include "net/base/io_buffer.h"
+#include "net/base/net_export.h"
+#include "net/base/request_priority.h"
+#include "net/log/net_log_with_source.h"
+#include "net/spdy/core/spdy_header_block.h"
+#include "net/ssl/token_binding.h"
+#include "net/websockets/websocket_basic_stream_adapters.h"
+#include "net/websockets/websocket_handshake_stream_base.h"
+#include "net/websockets/websocket_stream.h"
+
+namespace crypto {
+
+class ECPrivateKey;
+
+}  // namespace crypto
+
+namespace net {
+
+struct LoadTimingInfo;
+class SSLInfo;
+class IOBuffer;
+class SSLCertRequestInfo;
+class IPEndPoint;
+class HttpNetworkSession;
+struct NetErrorDetails;
+class HttpStream;
+class HttpResponseHeaders;
+struct HttpRequestInfo;
+class HttpResponseInfo;
+class SpdySession;
+struct AlternativeService;
+class SpdyStreamRequest;
+struct WebSocketExtensionParams;
+
+class NET_EXPORT_PRIVATE WebSocketHttp2HandshakeStream
+    : public WebSocketHandshakeStreamBase,
+      public WebSocketSpdyStreamAdapter::Delegate {
+ public:
+  // |connect_delegate| and |request| must out-live this object.
+  WebSocketHttp2HandshakeStream(
+      base::WeakPtr<SpdySession> session,
+      WebSocketStream::ConnectDelegate* connect_delegate,
+      std::vector<std::string> requested_sub_protocols,
+      std::vector<std::string> requested_extensions,
+      WebSocketStreamRequest* request);
+
+  ~WebSocketHttp2HandshakeStream() override;
+
+  // HttpStream methods.
+  int InitializeStream(const HttpRequestInfo* request_info,
+                       bool can_send_early,
+                       RequestPriority priority,
+                       const NetLogWithSource& net_log,
+                       CompletionOnceCallback callback) override;
+  int SendRequest(const HttpRequestHeaders& request_headers,
+                  HttpResponseInfo* response,
+                  CompletionOnceCallback callback) override;
+  int ReadResponseHeaders(CompletionOnceCallback callback) override;
+  int ReadResponseBody(IOBuffer* buf,
+                       int buf_len,
+                       CompletionOnceCallback callback) override;
+  void Close(bool not_reusable) override;
+  bool IsResponseBodyComplete() const override;
+  bool IsConnectionReused() const override;
+  void SetConnectionReused() override;
+  bool CanReuseConnection() const override;
+  int64_t GetTotalReceivedBytes() const override;
+  int64_t GetTotalSentBytes() const override;
+  bool GetAlternativeService(
+      AlternativeService* alternative_service) const override;
+  bool GetLoadTimingInfo(LoadTimingInfo* load_timing_info) const override;
+  void GetSSLInfo(SSLInfo* ssl_info) override;
+  void GetSSLCertRequestInfo(SSLCertRequestInfo* cert_request_info) override;
+  bool GetRemoteEndpoint(IPEndPoint* endpoint) override;
+  Error GetTokenBindingSignature(crypto::ECPrivateKey* key,
+                                 TokenBindingType tb_type,
+                                 std::vector<uint8_t>* out) override;
+  void Drain(HttpNetworkSession* session) override;
+  void SetPriority(RequestPriority priority) override;
+  void PopulateNetErrorDetails(NetErrorDetails* details) override;
+  HttpStream* RenewStreamForAuth() override;
+
+  // WebSocketHandshakeStreamBase methods.
+
+  // This is called from the top level once correct handshake response headers
+  // have been received. It creates an appropriate subclass of WebSocketStream
+  // depending on what extensions were negotiated. This object is unusable after
+  // Upgrade() has been called and should be disposed of as soon as possible.
+  std::unique_ptr<WebSocketStream> Upgrade() override;
+
+  // WebSocketSpdyStreamAdapter::Delegate methods.
+  void OnHeadersSent() override;
+  void OnHeadersReceived(const SpdyHeaderBlock& response_headers) override;
+  void OnClose(int status) override;
+
+  // Called by |spdy_stream_request_| when requested stream is ready.
+  void StartRequestCallback(int rv);
+
+ private:
+  // Validates the response and sends the finished handshake event.
+  int ValidateResponse();
+
+  // Check that the headers are well-formed and have a 200 status code,
+  // in which case returns OK, otherwise returns ERR_INVALID_RESPONSE.
+  int ValidateUpgradeResponse(const HttpResponseHeaders* headers);
+
+  void OnFinishOpeningHandshake();
+
+  void OnFailure(const std::string& message);
+
+  // The connection to open the Websocket stream on.
+  base::WeakPtr<SpdySession> session_;
+
+  // Owned by another object.
+  // |connect_delegate| will live during the lifetime of this object.
+  WebSocketStream::ConnectDelegate* const connect_delegate_;
+
+  HttpResponseInfo* http_response_info_;
+
+  SpdyHeaderBlock http2_request_headers_;
+
+  // The sub-protocols we requested.
+  std::vector<std::string> requested_sub_protocols_;
+
+  // The extensions we requested.
+  std::vector<std::string> requested_extensions_;
+
+  WebSocketStreamRequest* const stream_request_;
+
+  const HttpRequestInfo* request_info_;
+
+  RequestPriority priority_;
+
+  NetLogWithSource net_log_;
+
+  // SpdyStreamRequest that will create the stream.
+  std::unique_ptr<SpdyStreamRequest> spdy_stream_request_;
+
+  // SpdyStream corresponding to the request.
+  base::WeakPtr<SpdyStream> stream_;
+
+  // WebSocketSpdyStreamAdapter holding a WeakPtr to |stream_|.
+  // This can be passed on to WebSocketBasicStream when created.
+  std::unique_ptr<WebSocketSpdyStreamAdapter> stream_adapter_;
+
+  // True if |stream_| has been created then closed.
+  bool stream_closed_;
+
+  // The error code corresponding to the reason for closing the stream.
+  // Only meaningful if |stream_closed_| is true.
+  int stream_error_;
+
+  // True if complete response headers have been received.
+  bool response_headers_complete_;
+
+  // Save callback provided in asynchronous HttpStream methods.
+  CompletionOnceCallback callback_;
+
+  // The sub-protocol selected by the server.
+  std::string sub_protocol_;
+
+  // The extension(s) selected by the server.
+  std::string extensions_;
+
+  // The extension parameters. The class is defined in the implementation file
+  // to avoid including extension-related header files here.
+  std::unique_ptr<WebSocketExtensionParams> extension_params_;
+
+  DISALLOW_COPY_AND_ASSIGN(WebSocketHttp2HandshakeStream);
+};
+
+}  // namespace net
+
+#endif  // NET_WEBSOCKETS_WEBSOCKET_HTTP2_HANDSHAKE_STREAM_H_
diff --git a/net/websockets/websocket_test_util.cc b/net/websockets/websocket_test_util.cc
index 556ed74..38499ed 100644
--- a/net/websockets/websocket_test_util.cc
+++ b/net/websockets/websocket_test_util.cc
@@ -8,9 +8,11 @@
 #include <algorithm>
 #include <utility>
 
+#include "base/strings/string_util.h"
 #include "base/strings/stringprintf.h"
 #include "net/proxy_resolution/proxy_service.h"
 #include "net/socket/socket_test_util.h"
+#include "net/spdy/core/spdy_protocol.h"
 #include "net/websockets/websocket_basic_handshake_stream.h"
 #include "url/origin.h"
 
@@ -90,6 +92,42 @@
       extra_headers.c_str());
 }
 
+SpdyHeaderBlock WebSocketHttp2Request(
+    const std::string& path,
+    const std::string& authority,
+    const std::string& origin,
+    const WebSocketExtraHeaders& extra_headers) {
+  SpdyHeaderBlock request_headers;
+  request_headers[kHttp2MethodHeader] = "CONNECT";
+  request_headers[kHttp2AuthorityHeader] = authority;
+  request_headers[kHttp2SchemeHeader] = "https";
+  request_headers[kHttp2PathHeader] = path;
+  request_headers[kHttp2ProtocolHeader] = "websocket";
+  request_headers["pragma"] = "no-cache";
+  request_headers["cache-control"] = "no-cache";
+  request_headers["origin"] = origin;
+  request_headers["sec-websocket-version"] = "13";
+  request_headers["user-agent"] = "";
+  request_headers["accept-encoding"] = "gzip, deflate";
+  request_headers["accept-language"] = "en-us,fr";
+  request_headers["sec-websocket-extensions"] =
+      "permessage-deflate; client_max_window_bits";
+  for (const auto& header : extra_headers) {
+    request_headers[base::ToLowerASCII(header.first)] = header.second;
+  }
+  return request_headers;
+}
+
+SpdyHeaderBlock WebSocketHttp2Response(
+    const WebSocketExtraHeaders& extra_headers) {
+  SpdyHeaderBlock response_headers;
+  response_headers[kHttp2StatusHeader] = "200";
+  for (const auto& header : extra_headers) {
+    response_headers[base::ToLowerASCII(header.first)] = header.second;
+  }
+  return response_headers;
+}
+
 struct WebSocketMockClientSocketFactoryMaker::Detail {
   std::string expect_written;
   std::string return_to_read;
diff --git a/net/websockets/websocket_test_util.h b/net/websockets/websocket_test_util.h
index ef3674f0..a66262e 100644
--- a/net/websockets/websocket_test_util.h
+++ b/net/websockets/websocket_test_util.h
@@ -9,12 +9,14 @@
 
 #include <memory>
 #include <string>
+#include <utility>
 #include <vector>
 
 #include "base/macros.h"
 #include "net/http/http_basic_state.h"
 #include "net/http/http_stream_parser.h"
 #include "net/socket/client_socket_handle.h"
+#include "net/spdy/core/spdy_header_block.h"
 #include "net/traffic_annotation/network_traffic_annotation_test_helper.h"
 #include "net/url_request/url_request_test_util.h"
 #include "net/websockets/websocket_handshake_stream_create_helper.h"
@@ -26,6 +28,8 @@
 
 namespace net {
 
+using WebSocketExtraHeaders = std::vector<std::pair<std::string, std::string>>;
+
 class MockClientSocketFactory;
 class WebSocketBasicHandshakeStream;
 class ProxyResolutionService;
@@ -66,6 +70,18 @@
 // key. Each header in |extra_headers| must be terminated with "\r\n".
 std::string WebSocketStandardResponse(const std::string& extra_headers);
 
+// Generates a handshake request header block when using WebSockets over HTTP/2.
+SpdyHeaderBlock WebSocketHttp2Request(
+    const std::string& path,
+    const std::string& authority,
+    const std::string& origin,
+    const WebSocketExtraHeaders& extra_headers);
+
+// Generates a handshake response header block when using WebSockets over
+// HTTP/2.
+SpdyHeaderBlock WebSocketHttp2Response(
+    const WebSocketExtraHeaders& extra_headers);
+
 // This class provides a convenient way to construct a MockClientSocketFactory
 // for WebSocket tests.
 class WebSocketMockClientSocketFactoryMaker {
diff --git a/sandbox/mac/seatbelt_exec.cc b/sandbox/mac/seatbelt_exec.cc
index 5cab3a05..df8167c 100644
--- a/sandbox/mac/seatbelt_exec.cc
+++ b/sandbox/mac/seatbelt_exec.cc
@@ -21,6 +21,58 @@
 
 namespace sandbox {
 
+namespace {
+
+struct ReadTraits {
+  using BufferType = uint8_t*;
+  static constexpr char kNameString[] = "read";
+  static ssize_t Operate(int fd, BufferType buffer, size_t size) {
+    return read(fd, buffer, size);
+  }
+};
+constexpr char ReadTraits::kNameString[];
+
+struct WriteTraits {
+  using BufferType = const uint8_t*;
+  static constexpr char kNameString[] = "write";
+  static ssize_t Operate(int fd, BufferType buffer, size_t size) {
+    return write(fd, buffer, size);
+  }
+};
+constexpr char WriteTraits::kNameString[];
+
+template <typename Traits>
+bool ReadOrWrite(int fd,
+                 const typename Traits::BufferType buffer,
+                 const size_t size) {
+  if (size > std::numeric_limits<ssize_t>::max()) {
+    logging::Error("request size is greater than ssize_t::max");
+    return false;
+  }
+
+  ssize_t bytes_to_transact = static_cast<ssize_t>(size);
+
+  while (bytes_to_transact > 0) {
+    ssize_t offset = size - bytes_to_transact;
+    ssize_t transacted_bytes =
+        HANDLE_EINTR(Traits::Operate(fd, buffer + offset, bytes_to_transact));
+    if (transacted_bytes < 0) {
+      if (errno == EAGAIN) {
+        sched_yield();
+        continue;
+      }
+      logging::PError("%s failed", Traits::kNameString);
+      return false;
+    }
+
+    bytes_to_transact -= transacted_bytes;
+  }
+
+  return true;
+}
+
+}  // namespace
+
 SeatbeltExecClient::SeatbeltExecClient() {
   if (pipe(pipe_) != 0)
     logging::PFatal("SeatbeltExecClient: pipe failed");
@@ -82,23 +134,16 @@
 
 bool SeatbeltExecClient::WriteString(const std::string& str) {
   uint64_t str_len = static_cast<uint64_t>(str.size());
-
-  if (HANDLE_EINTR(write(pipe_[1], &str_len, sizeof(str_len))) !=
-      sizeof(str_len)) {
-    logging::PError("SeatbeltExecClient: write size of buffer failed");
+  if (!ReadOrWrite<WriteTraits>(pipe_[1], reinterpret_cast<uint8_t*>(&str_len),
+                                sizeof(str_len))) {
+    logging::Error("SeatbeltExecClient: write buffer length failed.");
     return false;
   }
 
-  uint64_t bytes_written = 0;
-
-  while (bytes_written < str_len) {
-    ssize_t wrote_this_pass = HANDLE_EINTR(
-        write(pipe_[1], &str[bytes_written], str_len - bytes_written));
-    if (wrote_this_pass < 0) {
-      logging::PError("SeatbeltExecClient: write failed");
-      return false;
-    }
-    bytes_written += wrote_this_pass;
+  if (!ReadOrWrite<WriteTraits>(
+          pipe_[1], reinterpret_cast<const uint8_t*>(&str[0]), str_len)) {
+    logging::Error("SeatbeltExecClient: write buffer failed.");
+    return false;
   }
 
   return true;
@@ -151,24 +196,18 @@
 
 bool SeatbeltExecServer::ReadString(std::string* str) {
   uint64_t buf_len = 0;
-  if (HANDLE_EINTR(read(fd_, &buf_len, sizeof(buf_len))) != sizeof(buf_len)) {
-    logging::PError("SeatbeltExecServer: read buffer length failed");
+  if (!ReadOrWrite<ReadTraits>(fd_, reinterpret_cast<uint8_t*>(&buf_len),
+                               sizeof(buf_len))) {
+    logging::Error("SeatbeltExecServer: failed to read buffer length.");
     return false;
   }
 
-  str->clear();
   str->resize(buf_len);
 
-  uint64_t bytes_read = 0;
-
-  while (bytes_read < buf_len) {
-    ssize_t read_this_pass =
-        HANDLE_EINTR(read(fd_, &(*str)[bytes_read], buf_len - bytes_read));
-    if (read_this_pass < 0) {
-      logging::PError("SeatbeltExecServer: read failed");
-      return false;
-    }
-    bytes_read += read_this_pass;
+  if (!ReadOrWrite<ReadTraits>(fd_, reinterpret_cast<uint8_t*>(&(*str)[0]),
+                               buf_len)) {
+    logging::Error("SeatbeltExecServer: failed to read buffer.");
+    return false;
   }
 
   return true;
diff --git a/services/identity/public/cpp/identity_manager.h b/services/identity/public/cpp/identity_manager.h
index c0c05cd..cd7fabbc 100644
--- a/services/identity/public/cpp/identity_manager.h
+++ b/services/identity/public/cpp/identity_manager.h
@@ -15,6 +15,9 @@
 #include "components/signin/core/browser/signin_manager.h"
 #endif
 
+// Necessary to declare this class as a friend.
+class ProfileSyncServiceHarness;
+
 // Necessary to declare functions in identity_test_utils.h as friends.
 class FakeSigninManagerBase;
 class FakeSigninManager;
@@ -110,6 +113,7 @@
       ProfileOAuth2TokenService* token_service,
       IdentityManager* identity_manager,
       const std::string& email);
+  friend ProfileSyncServiceHarness;
 
   // Sets the primary account info synchronously with both the IdentityManager
   // and its backing SigninManager/ProfileOAuth2TokenService instances.
diff --git a/services/network/network_context_unittest.cc b/services/network/network_context_unittest.cc
index 945f48c..fc32e4a 100644
--- a/services/network/network_context_unittest.cc
+++ b/services/network/network_context_unittest.cc
@@ -615,9 +615,9 @@
     // Kick the ProxyResolutionService into action, as it doesn't start updating
     // its config until it's first used.
     proxy_resolution_service->ForceReloadProxyConfig();
-    EXPECT_TRUE(proxy_resolution_service->config().is_valid());
+    EXPECT_TRUE(proxy_resolution_service->config());
     EXPECT_TRUE(
-        proxy_resolution_service->config().Equals(initial_proxy_config));
+        proxy_resolution_service->config()->Equals(initial_proxy_config));
 
     // Always go through the other configs in the same order. This has the
     // advantage of testing the case where there's no change, for
@@ -625,8 +625,8 @@
     for (const auto& proxy_config : proxy_configs) {
       config_client->OnProxyConfigUpdated(proxy_config);
       scoped_task_environment_.RunUntilIdle();
-      EXPECT_TRUE(proxy_resolution_service->config().is_valid());
-      EXPECT_TRUE(proxy_resolution_service->config().Equals(proxy_config));
+      EXPECT_TRUE(proxy_resolution_service->config());
+      EXPECT_TRUE(proxy_resolution_service->config()->Equals(proxy_config));
     }
   }
 }
@@ -646,8 +646,8 @@
   // Kick the ProxyResolutionService into action, as it doesn't start updating
   // its config until it's first used.
   proxy_resolution_service->ForceReloadProxyConfig();
-  EXPECT_TRUE(proxy_resolution_service->config().is_valid());
-  EXPECT_TRUE(proxy_resolution_service->config().Equals(proxy_config));
+  EXPECT_TRUE(proxy_resolution_service->config());
+  EXPECT_TRUE(proxy_resolution_service->config()->Equals(proxy_config));
 }
 
 TEST_F(NetworkContextTest, NoInitialProxyConfig) {
@@ -661,8 +661,8 @@
 
   net::ProxyResolutionService* proxy_resolution_service =
       network_context->GetURLRequestContext()->proxy_resolution_service();
-  EXPECT_FALSE(proxy_resolution_service->config().is_valid());
-  EXPECT_FALSE(proxy_resolution_service->fetched_config().is_valid());
+  EXPECT_FALSE(proxy_resolution_service->config());
+  EXPECT_FALSE(proxy_resolution_service->fetched_config());
 
   // Before there's a proxy configuration, proxy requests should hang.
   net::ProxyInfo proxy_info;
@@ -673,8 +673,8 @@
                                      test_callback.callback(), &request,
                                      nullptr, net::NetLogWithSource()));
   scoped_task_environment_.RunUntilIdle();
-  EXPECT_FALSE(proxy_resolution_service->config().is_valid());
-  EXPECT_FALSE(proxy_resolution_service->fetched_config().is_valid());
+  EXPECT_FALSE(proxy_resolution_service->config());
+  EXPECT_FALSE(proxy_resolution_service->fetched_config());
   ASSERT_FALSE(test_callback.have_result());
 
   net::ProxyConfig proxy_config;
diff --git a/services/network/proxy_resolving_client_socket.cc b/services/network/proxy_resolving_client_socket.cc
index 89862eb7..a08647f8 100644
--- a/services/network/proxy_resolving_client_socket.cc
+++ b/services/network/proxy_resolving_client_socket.cc
@@ -333,33 +333,19 @@
         proxy_info_.proxy_server().host_port_pair());
   }
 
-  int rv =
-      network_session_->proxy_resolution_service()->ReconsiderProxyAfterError(
-          url_, std::string(), error, &proxy_info_,
-          base::BindRepeating(&ProxyResolvingClientSocket::ConnectToProxy,
-                              base::Unretained(this)),
-          &proxy_resolve_request_, nullptr, net_log_);
-  if (rv == net::OK || rv == net::ERR_IO_PENDING) {
-    CloseTransportSocket();
-  } else {
-    // If ReconsiderProxyAfterError() failed synchronously, it means
-    // there was nothing left to fall-back to, so fail the transaction
-    // with the last connection error we got.
-    rv = error;
-  }
+  // There was nothing left to fall-back to, so fail the transaction
+  // with the last connection error we got.
+  if (!proxy_info_.Fallback(error, net_log_))
+    return error;
 
-  // We either have new proxy info or there was an error in falling back.
-  // In both cases we want to post ConnectToProxy (in the error case
-  // we might still want to fall back a direct connection).
-  if (rv != net::ERR_IO_PENDING) {
-    base::ThreadTaskRunnerHandle::Get()->PostTask(
-        FROM_HERE, base::BindOnce(&ProxyResolvingClientSocket::ConnectToProxy,
-                                  weak_factory_.GetWeakPtr(), rv));
-    // Since we potentially have another try to go (trying the direct connect)
-    // set the return code code to ERR_IO_PENDING.
-    rv = net::ERR_IO_PENDING;
-  }
-  return rv;
+  CloseTransportSocket();
+
+  base::ThreadTaskRunnerHandle::Get()->PostTask(
+      FROM_HERE, base::BindOnce(&ProxyResolvingClientSocket::ConnectToProxy,
+                                weak_factory_.GetWeakPtr(), net::OK));
+  // Since we potentially have another try to go, set the return code code to
+  // ERR_IO_PENDING.
+  return net::ERR_IO_PENDING;
 }
 
 }  // namespace network
diff --git a/services/network/proxy_resolving_client_socket_unittest.cc b/services/network/proxy_resolving_client_socket_unittest.cc
index 1a24bc87..27d3937 100644
--- a/services/network/proxy_resolving_client_socket_unittest.cc
+++ b/services/network/proxy_resolving_client_socket_unittest.cc
@@ -569,49 +569,6 @@
             resolver.pending_jobs()[0]->url());
 }
 
-TEST_F(ProxyResolvingClientSocketTest, ProxyConfigChanged) {
-  auto context = std::make_unique<net::TestURLRequestContext>(true);
-  // Use direct connection.
-  std::unique_ptr<net::ProxyResolutionService> proxy_resolution_service =
-      net::ProxyResolutionService::CreateDirect();
-  context->set_proxy_resolution_service(proxy_resolution_service.get());
-  context->Init();
-
-  net::MockClientSocketFactory socket_factory;
-
-  // There should be two connection attempts because ProxyConfig has changed
-  // midway through connect.
-  net::StaticSocketDataProvider data_1;
-  data_1.set_connect_data(
-      net::MockConnect(net::SYNCHRONOUS, net::ERR_CONNECTION_REFUSED));
-  socket_factory.AddSocketDataProvider(&data_1);
-
-  net::StaticSocketDataProvider data_2;
-  data_2.set_connect_data(net::MockConnect(net::SYNCHRONOUS, net::OK));
-  socket_factory.AddSocketDataProvider(&data_2);
-
-  GURL url("http://www.example.com");
-  ProxyResolvingClientSocketFactory proxy_resolving_socket_factory(
-      &socket_factory, context.get());
-  std::unique_ptr<ProxyResolvingClientSocket> socket =
-      proxy_resolving_socket_factory.CreateSocket(net::SSLConfig(), url);
-
-  net::TestCompletionCallback callback;
-  int status = socket->Connect(callback.callback());
-  EXPECT_EQ(net::ERR_IO_PENDING, status);
-
-  // Calling ForceReloadProxyConfig will cause the proxy configuration to
-  // change. It will still be the direct connection but the configuration
-  // version will be bumped. That is enough for the job controller to restart
-  // the jobs.
-  proxy_resolution_service->ForceReloadProxyConfig();
-  EXPECT_EQ(net::OK, callback.WaitForResult());
-  EXPECT_TRUE(data_1.AllReadDataConsumed());
-  EXPECT_TRUE(data_1.AllWriteDataConsumed());
-  EXPECT_TRUE(data_2.AllReadDataConsumed());
-  EXPECT_TRUE(data_2.AllWriteDataConsumed());
-}
-
 class ReconsiderProxyAfterErrorTest
     : public testing::Test,
       public testing::WithParamInterface<::testing::tuple<bool, int>> {
diff --git a/services/network/public/cpp/proxy_config_mojom_traits.cc b/services/network/public/cpp/proxy_config_mojom_traits.cc
index a9bb40bb..019e41c 100644
--- a/services/network/public/cpp/proxy_config_mojom_traits.cc
+++ b/services/network/public/cpp/proxy_config_mojom_traits.cc
@@ -179,7 +179,6 @@
 
   out_proxy_config->set_auto_detect(data.auto_detect());
   out_proxy_config->set_pac_mandatory(data.pac_mandatory());
-  out_proxy_config->set_id(data.id());
   return true;
 }
 
diff --git a/services/network/public/cpp/proxy_config_mojom_traits.h b/services/network/public/cpp/proxy_config_mojom_traits.h
index 1622d74..f5ba70d8 100644
--- a/services/network/public/cpp/proxy_config_mojom_traits.h
+++ b/services/network/public/cpp/proxy_config_mojom_traits.h
@@ -115,7 +115,6 @@
   static net::ProxyConfigSource source(const net::ProxyConfig& r) {
     return r.source();
   }
-  static int32_t id(const net::ProxyConfig& r) { return r.id(); }
   static bool Read(network::mojom::ProxyConfigDataView data,
                    net::ProxyConfig* out_proxy_config);
 };
diff --git a/services/network/public/cpp/proxy_config_mojom_traits_unittest.cc b/services/network/public/cpp/proxy_config_mojom_traits_unittest.cc
index 535a4d57..c17ba4ba 100644
--- a/services/network/public/cpp/proxy_config_mojom_traits_unittest.cc
+++ b/services/network/public/cpp/proxy_config_mojom_traits_unittest.cc
@@ -21,21 +21,17 @@
       mojom::ProxyConfig::Serialize(&original_config), &copied_config));
 
   return original_config.Equals(copied_config) &&
-         original_config.id() == copied_config.id() &&
-         original_config.source() == copied_config.source() &&
-         original_config.is_valid() == copied_config.is_valid();
+         original_config.source() == copied_config.source();
 }
 
 TEST(ProxyConfigTraitsTest, AutoDetect) {
   net::ProxyConfig proxy_config = net::ProxyConfig::CreateAutoDetect();
-  proxy_config.set_id(1);
   proxy_config.set_source(net::ProxyConfigSource::PROXY_CONFIG_SOURCE_KDE);
   EXPECT_TRUE(TestProxyConfigRoundTrip(proxy_config));
 }
 
 TEST(ProxyConfigTraitsTest, Direct) {
   net::ProxyConfig proxy_config = net::ProxyConfig::CreateDirect();
-  proxy_config.set_id(2);
   proxy_config.set_source(
       net::ProxyConfigSource::PROXY_CONFIG_SOURCE_GSETTINGS);
   EXPECT_TRUE(TestProxyConfigRoundTrip(proxy_config));
diff --git a/services/network/public/mojom/proxy_config.mojom b/services/network/public/mojom/proxy_config.mojom
index fea1cd2..232cba7 100644
--- a/services/network/public/mojom/proxy_config.mojom
+++ b/services/network/public/mojom/proxy_config.mojom
@@ -55,8 +55,6 @@
   bool pac_mandatory;
   ProxyRules proxy_rules;
   ProxyConfigSource source;
-  // TODO(mmenke): This is a historical wart. Remove it.
-  int32 id;
 };
 
 // Interface for pushing proxy configuration updates to a NetworkContext.
diff --git a/services/ui/demo/BUILD.gn b/services/ui/demo/BUILD.gn
index 6b8b004..0ccd440 100644
--- a/services/ui/demo/BUILD.gn
+++ b/services/ui/demo/BUILD.gn
@@ -89,6 +89,7 @@
   deps = [
     ":demo",
     "//base",
+    "//base/test:test_support",
     "//services/service_manager/public/cpp",
     "//services/service_manager/public/cpp:service_test_support",
     "//services/ui/public/interfaces",
diff --git a/services/ui/demo/mus_demo_unittests.cc b/services/ui/demo/mus_demo_unittests.cc
index b2cfc99..d7fe8039 100644
--- a/services/ui/demo/mus_demo_unittests.cc
+++ b/services/ui/demo/mus_demo_unittests.cc
@@ -2,14 +2,17 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+#include "base/base_switches.h"
 #include "base/bind.h"
 #include "base/command_line.h"
 #include "base/macros.h"
 #include "base/run_loop.h"
+#include "base/test/scoped_feature_list.h"
 #include "services/service_manager/public/cpp/service_test.h"
 #include "services/ui/public/interfaces/constants.mojom.h"
 #include "services/ui/public/interfaces/window_server_test.mojom.h"
 #include "testing/gtest/include/gtest/gtest.h"
+#include "ui/base/ui_base_features.h"
 #include "ui/base/ui_base_switches.h"
 
 namespace ui {
@@ -30,14 +33,16 @@
   ~MusDemoTest() override {}
 
   void SetUp() override {
+    feature_list_.InitAndEnableFeature(features::kMash);
     base::CommandLine::ForCurrentProcess()->AppendSwitch("use-test-config");
-    base::CommandLine::ForCurrentProcess()->AppendSwitch(switches::kMus);
-    base::CommandLine::ForCurrentProcess()->AppendSwitch(
-        switches::kMusHostingViz);
+    base::CommandLine::ForCurrentProcess()->AppendSwitchASCII(
+        switches::kEnableFeatures, features::kMash.name);
     ServiceTest::SetUp();
   }
 
  private:
+  base::test::ScopedFeatureList feature_list_;
+
   DISALLOW_COPY_AND_ASSIGN(MusDemoTest);
 };
 
diff --git a/services/ui/public/interfaces/BUILD.gn b/services/ui/public/interfaces/BUILD.gn
index 7465988..68559b6 100644
--- a/services/ui/public/interfaces/BUILD.gn
+++ b/services/ui/public/interfaces/BUILD.gn
@@ -6,8 +6,8 @@
 import("//testing/test.gni")
 
 mojom("interfaces") {
-  # The window service runs in the browser process for --mus and in the
-  # ash_and_ui process for --mash. Allow IPC serialization to be skipped for
+  # The window service runs in the browser process for mus and in the
+  # ash_and_ui process for mash. Allow IPC serialization to be skipped for
   # the common case of in-process mojo calls. This causes a ~130KB size
   # increase on 64-bit Intel builds.
   if (is_chromeos) {
diff --git a/services/ui/service.cc b/services/ui/service.cc
index 1a2cf7d8..266e26de 100644
--- a/services/ui/service.cc
+++ b/services/ui/service.cc
@@ -245,7 +245,7 @@
   if (should_host_viz_) {
     // If mus is hosting viz, then it needs to set up ozone so that it can
     // connect to the gpu service through the connector.
-    // Currently mus hosting viz (i.e. --mash mode) only runs single-process.
+    // Currently mus hosting viz (i.e. mash mode) only runs single-process.
     params.connector = context()->connector();
     params.single_process = true;
     params.using_mojo = true;
diff --git a/services/ui/ws/BUILD.gn b/services/ui/ws/BUILD.gn
index 27585d1..3d73172 100644
--- a/services/ui/ws/BUILD.gn
+++ b/services/ui/ws/BUILD.gn
@@ -224,6 +224,7 @@
   deps = [
     "//base",
     "//base/test:test_config",
+    "//base/test:test_support",
     "//components/viz/test:test_support",
     "//mojo/common",
     "//mojo/public/cpp/bindings:bindings",
diff --git a/services/ui/ws/window_server_test_base.cc b/services/ui/ws/window_server_test_base.cc
index 39ee5072..fc19a86 100644
--- a/services/ui/ws/window_server_test_base.cc
+++ b/services/ui/ws/window_server_test_base.cc
@@ -16,6 +16,7 @@
 #include "ui/aura/env.h"
 #include "ui/aura/mus/window_tree_client.h"
 #include "ui/aura/mus/window_tree_host_mus.h"
+#include "ui/base/ui_base_features.h"
 #include "ui/base/ui_base_switches.h"
 #include "ui/display/display.h"
 #include "ui/display/display_list.h"
@@ -93,9 +94,7 @@
 }
 
 void WindowServerTestBase::SetUp() {
-  base::CommandLine::ForCurrentProcess()->AppendSwitch(switches::kMus);
-  base::CommandLine::ForCurrentProcess()->AppendSwitch(
-      switches::kMusHostingViz);
+  feature_list_.InitAndEnableFeature(features::kMash);
   WindowServerServiceTestBase::SetUp();
 
   env_ = aura::Env::CreateInstance(aura::Env::Mode::MUS);
diff --git a/services/ui/ws/window_server_test_base.h b/services/ui/ws/window_server_test_base.h
index fce9196f..df9450766e 100644
--- a/services/ui/ws/window_server_test_base.h
+++ b/services/ui/ws/window_server_test_base.h
@@ -9,6 +9,7 @@
 #include <set>
 
 #include "base/macros.h"
+#include "base/test/scoped_feature_list.h"
 #include "services/service_manager/public/cpp/binder_registry.h"
 #include "services/ui/public/interfaces/window_tree.mojom.h"
 #include "services/ui/ws/window_server_service_test_base.h"
@@ -141,6 +142,8 @@
   // |window_tree_host| is not deleted.
   bool DeleteWindowTreeHost(aura::WindowTreeHostMus* window_tree_host);
 
+  base::test::ScopedFeatureList feature_list_;
+
   std::unique_ptr<aura::Env> env_;
   ::wm::WMState wm_state_;
   display::ScreenBase screen_;
diff --git a/services/viz/public/cpp/compositing/compositor_frame_metadata_struct_traits.cc b/services/viz/public/cpp/compositing/compositor_frame_metadata_struct_traits.cc
index 1991ceb..dda926f 100644
--- a/services/viz/public/cpp/compositing/compositor_frame_metadata_struct_traits.cc
+++ b/services/viz/public/cpp/compositing/compositor_frame_metadata_struct_traits.cc
@@ -29,7 +29,6 @@
 
   out->min_page_scale_factor = data.min_page_scale_factor();
   out->max_page_scale_factor = data.max_page_scale_factor();
-  out->root_overflow_x_hidden = data.root_overflow_x_hidden();
   out->root_overflow_y_hidden = data.root_overflow_y_hidden();
   out->may_contain_video = data.may_contain_video();
   out->is_resourceless_software_draw_with_scroll_or_animation =
diff --git a/services/viz/public/cpp/compositing/compositor_frame_metadata_struct_traits.h b/services/viz/public/cpp/compositing/compositor_frame_metadata_struct_traits.h
index 75211f4c..8211350b 100644
--- a/services/viz/public/cpp/compositing/compositor_frame_metadata_struct_traits.h
+++ b/services/viz/public/cpp/compositing/compositor_frame_metadata_struct_traits.h
@@ -51,11 +51,6 @@
     return metadata.max_page_scale_factor;
   }
 
-  static bool root_overflow_x_hidden(
-      const viz::CompositorFrameMetadata& metadata) {
-    return metadata.root_overflow_x_hidden;
-  }
-
   static bool root_overflow_y_hidden(
       const viz::CompositorFrameMetadata& metadata) {
     return metadata.root_overflow_y_hidden;
diff --git a/services/viz/public/cpp/compositing/struct_traits_unittest.cc b/services/viz/public/cpp/compositing/struct_traits_unittest.cc
index bfc445d..7433f9f 100644
--- a/services/viz/public/cpp/compositing/struct_traits_unittest.cc
+++ b/services/viz/public/cpp/compositing/struct_traits_unittest.cc
@@ -620,7 +620,6 @@
   const gfx::SizeF root_layer_size(1234.5f, 5432.1f);
   const float min_page_scale_factor = 3.5f;
   const float max_page_scale_factor = 4.6f;
-  const bool root_overflow_x_hidden = true;
   const bool root_overflow_y_hidden = true;
   const bool may_contain_video = true;
   const bool is_resourceless_software_draw_with_scroll_or_animation = true;
@@ -663,7 +662,6 @@
   input.root_layer_size = root_layer_size;
   input.min_page_scale_factor = min_page_scale_factor;
   input.max_page_scale_factor = max_page_scale_factor;
-  input.root_overflow_x_hidden = root_overflow_x_hidden;
   input.root_overflow_y_hidden = root_overflow_y_hidden;
   input.may_contain_video = may_contain_video;
   input.is_resourceless_software_draw_with_scroll_or_animation =
@@ -690,7 +688,6 @@
   EXPECT_EQ(root_layer_size, output.root_layer_size);
   EXPECT_EQ(min_page_scale_factor, output.min_page_scale_factor);
   EXPECT_EQ(max_page_scale_factor, output.max_page_scale_factor);
-  EXPECT_EQ(root_overflow_x_hidden, output.root_overflow_x_hidden);
   EXPECT_EQ(root_overflow_y_hidden, output.root_overflow_y_hidden);
   EXPECT_EQ(may_contain_video, output.may_contain_video);
   EXPECT_EQ(is_resourceless_software_draw_with_scroll_or_animation,
diff --git a/services/viz/public/interfaces/compositing/compositor_frame_metadata.mojom b/services/viz/public/interfaces/compositing/compositor_frame_metadata.mojom
index 32d760d..51095e3 100644
--- a/services/viz/public/interfaces/compositing/compositor_frame_metadata.mojom
+++ b/services/viz/public/interfaces/compositing/compositor_frame_metadata.mojom
@@ -20,7 +20,6 @@
   gfx.mojom.SizeF root_layer_size;
   float min_page_scale_factor;
   float max_page_scale_factor;
-  bool root_overflow_x_hidden;
   bool root_overflow_y_hidden;
   bool may_contain_video;
   bool is_resourceless_software_draw_with_scroll_or_animation;
diff --git a/testing/buildbot/chromium.android.fyi.json b/testing/buildbot/chromium.android.fyi.json
index 82ff753..0f79e14 100644
--- a/testing/buildbot/chromium.android.fyi.json
+++ b/testing/buildbot/chromium.android.fyi.json
@@ -4422,7 +4422,7 @@
             "--bucket",
             "chromium-result-details",
             "--test-name",
-            "mojo_public_bindings_unittests"
+            "mojo_unittests"
           ],
           "script": "//build/android/pylib/results/presentation/test_results_presentation.py"
         },
@@ -4434,55 +4434,7 @@
             }
           ]
         },
-        "test": "mojo_public_bindings_unittests"
-      },
-      {
-        "args": [
-          "--gs-results-bucket=chromium-result-details",
-          "--recover-devices"
-        ],
-        "merge": {
-          "args": [
-            "--bucket",
-            "chromium-result-details",
-            "--test-name",
-            "mojo_public_system_unittests"
-          ],
-          "script": "//build/android/pylib/results/presentation/test_results_presentation.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "device_type": "coho"
-            }
-          ]
-        },
-        "test": "mojo_public_system_unittests"
-      },
-      {
-        "args": [
-          "--gs-results-bucket=chromium-result-details",
-          "--recover-devices"
-        ],
-        "merge": {
-          "args": [
-            "--bucket",
-            "chromium-result-details",
-            "--test-name",
-            "mojo_system_unittests"
-          ],
-          "script": "//build/android/pylib/results/presentation/test_results_presentation.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "device_type": "coho"
-            }
-          ]
-        },
-        "test": "mojo_system_unittests"
+        "test": "mojo_unittests"
       },
       {
         "args": [
@@ -5362,57 +5314,7 @@
             "--bucket",
             "chromium-result-details",
             "--test-name",
-            "mojo_public_bindings_unittests"
-          ],
-          "script": "//build/android/pylib/results/presentation/test_results_presentation.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "device_type": "gce_x86"
-            }
-          ],
-          "hard_timeout": 60
-        },
-        "test": "mojo_public_bindings_unittests"
-      },
-      {
-        "args": [
-          "--gs-results-bucket=chromium-result-details",
-          "--recover-devices"
-        ],
-        "merge": {
-          "args": [
-            "--bucket",
-            "chromium-result-details",
-            "--test-name",
-            "mojo_public_system_unittests"
-          ],
-          "script": "//build/android/pylib/results/presentation/test_results_presentation.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "device_type": "gce_x86"
-            }
-          ],
-          "hard_timeout": 60
-        },
-        "test": "mojo_public_system_unittests"
-      },
-      {
-        "args": [
-          "--gs-results-bucket=chromium-result-details",
-          "--recover-devices"
-        ],
-        "merge": {
-          "args": [
-            "--bucket",
-            "chromium-result-details",
-            "--test-name",
-            "mojo_system_unittests"
+            "mojo_unittests"
           ],
           "script": "//build/android/pylib/results/presentation/test_results_presentation.py"
         },
@@ -5425,7 +5327,7 @@
           ],
           "hard_timeout": 120
         },
-        "test": "mojo_system_unittests"
+        "test": "mojo_unittests"
       },
       {
         "args": [
diff --git a/testing/buildbot/chromium.android.json b/testing/buildbot/chromium.android.json
index ed67c18..16e3a754 100644
--- a/testing/buildbot/chromium.android.json
+++ b/testing/buildbot/chromium.android.json
@@ -1586,135 +1586,6 @@
             "--bucket",
             "chromium-result-details",
             "--test-name",
-            "mojo_public_bindings_unittests"
-          ],
-          "script": "//build/android/pylib/results/presentation/test_results_presentation.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "cipd_packages": [
-            {
-              "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
-              "location": "bin",
-              "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c"
-            }
-          ],
-          "dimension_sets": [
-            {
-              "device_os": "KTU84P",
-              "device_type": "hammerhead"
-            }
-          ],
-          "hard_timeout": 60,
-          "output_links": [
-            {
-              "link": [
-                "https://luci-logdog.appspot.com/v/?s",
-                "=android%2Fswarming%2Flogcats%2F",
-                "${TASK_ID}%2F%2B%2Funified_logcats"
-              ],
-              "name": "shard #${SHARD_INDEX} logcats"
-            }
-          ]
-        },
-        "test": "mojo_public_bindings_unittests"
-      },
-      {
-        "args": [
-          "--gs-results-bucket=chromium-result-details",
-          "--recover-devices"
-        ],
-        "merge": {
-          "args": [
-            "--bucket",
-            "chromium-result-details",
-            "--test-name",
-            "mojo_public_system_unittests"
-          ],
-          "script": "//build/android/pylib/results/presentation/test_results_presentation.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "cipd_packages": [
-            {
-              "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
-              "location": "bin",
-              "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c"
-            }
-          ],
-          "dimension_sets": [
-            {
-              "device_os": "KTU84P",
-              "device_type": "hammerhead"
-            }
-          ],
-          "hard_timeout": 60,
-          "output_links": [
-            {
-              "link": [
-                "https://luci-logdog.appspot.com/v/?s",
-                "=android%2Fswarming%2Flogcats%2F",
-                "${TASK_ID}%2F%2B%2Funified_logcats"
-              ],
-              "name": "shard #${SHARD_INDEX} logcats"
-            }
-          ]
-        },
-        "test": "mojo_public_system_unittests"
-      },
-      {
-        "args": [
-          "--gs-results-bucket=chromium-result-details",
-          "--recover-devices"
-        ],
-        "merge": {
-          "args": [
-            "--bucket",
-            "chromium-result-details",
-            "--test-name",
-            "mojo_system_unittests"
-          ],
-          "script": "//build/android/pylib/results/presentation/test_results_presentation.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "cipd_packages": [
-            {
-              "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
-              "location": "bin",
-              "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c"
-            }
-          ],
-          "dimension_sets": [
-            {
-              "device_os": "KTU84P",
-              "device_type": "hammerhead"
-            }
-          ],
-          "hard_timeout": 180,
-          "output_links": [
-            {
-              "link": [
-                "https://luci-logdog.appspot.com/v/?s",
-                "=android%2Fswarming%2Flogcats%2F",
-                "${TASK_ID}%2F%2B%2Funified_logcats"
-              ],
-              "name": "shard #${SHARD_INDEX} logcats"
-            }
-          ]
-        },
-        "test": "mojo_system_unittests"
-      },
-      {
-        "args": [
-          "--gs-results-bucket=chromium-result-details",
-          "--recover-devices"
-        ],
-        "merge": {
-          "args": [
-            "--bucket",
-            "chromium-result-details",
-            "--test-name",
             "mojo_test_apk"
           ],
           "script": "//build/android/pylib/results/presentation/test_results_presentation.py"
@@ -1758,6 +1629,49 @@
             "--bucket",
             "chromium-result-details",
             "--test-name",
+            "mojo_unittests"
+          ],
+          "script": "//build/android/pylib/results/presentation/test_results_presentation.py"
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "cipd_packages": [
+            {
+              "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
+              "location": "bin",
+              "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c"
+            }
+          ],
+          "dimension_sets": [
+            {
+              "device_os": "KTU84P",
+              "device_type": "hammerhead"
+            }
+          ],
+          "hard_timeout": 180,
+          "output_links": [
+            {
+              "link": [
+                "https://luci-logdog.appspot.com/v/?s",
+                "=android%2Fswarming%2Flogcats%2F",
+                "${TASK_ID}%2F%2B%2Funified_logcats"
+              ],
+              "name": "shard #${SHARD_INDEX} logcats"
+            }
+          ]
+        },
+        "test": "mojo_unittests"
+      },
+      {
+        "args": [
+          "--gs-results-bucket=chromium-result-details",
+          "--recover-devices"
+        ],
+        "merge": {
+          "args": [
+            "--bucket",
+            "chromium-result-details",
+            "--test-name",
             "net_unittests"
           ],
           "script": "//build/android/pylib/results/presentation/test_results_presentation.py"
@@ -3268,135 +3182,6 @@
             "--bucket",
             "chromium-result-details",
             "--test-name",
-            "mojo_public_bindings_unittests"
-          ],
-          "script": "//build/android/pylib/results/presentation/test_results_presentation.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "cipd_packages": [
-            {
-              "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
-              "location": "bin",
-              "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c"
-            }
-          ],
-          "dimension_sets": [
-            {
-              "device_os": "KTU84P",
-              "device_type": "hammerhead"
-            }
-          ],
-          "hard_timeout": 60,
-          "output_links": [
-            {
-              "link": [
-                "https://luci-logdog.appspot.com/v/?s",
-                "=android%2Fswarming%2Flogcats%2F",
-                "${TASK_ID}%2F%2B%2Funified_logcats"
-              ],
-              "name": "shard #${SHARD_INDEX} logcats"
-            }
-          ]
-        },
-        "test": "mojo_public_bindings_unittests"
-      },
-      {
-        "args": [
-          "--gs-results-bucket=chromium-result-details",
-          "--recover-devices"
-        ],
-        "merge": {
-          "args": [
-            "--bucket",
-            "chromium-result-details",
-            "--test-name",
-            "mojo_public_system_unittests"
-          ],
-          "script": "//build/android/pylib/results/presentation/test_results_presentation.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "cipd_packages": [
-            {
-              "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
-              "location": "bin",
-              "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c"
-            }
-          ],
-          "dimension_sets": [
-            {
-              "device_os": "KTU84P",
-              "device_type": "hammerhead"
-            }
-          ],
-          "hard_timeout": 60,
-          "output_links": [
-            {
-              "link": [
-                "https://luci-logdog.appspot.com/v/?s",
-                "=android%2Fswarming%2Flogcats%2F",
-                "${TASK_ID}%2F%2B%2Funified_logcats"
-              ],
-              "name": "shard #${SHARD_INDEX} logcats"
-            }
-          ]
-        },
-        "test": "mojo_public_system_unittests"
-      },
-      {
-        "args": [
-          "--gs-results-bucket=chromium-result-details",
-          "--recover-devices"
-        ],
-        "merge": {
-          "args": [
-            "--bucket",
-            "chromium-result-details",
-            "--test-name",
-            "mojo_system_unittests"
-          ],
-          "script": "//build/android/pylib/results/presentation/test_results_presentation.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "cipd_packages": [
-            {
-              "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
-              "location": "bin",
-              "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c"
-            }
-          ],
-          "dimension_sets": [
-            {
-              "device_os": "KTU84P",
-              "device_type": "hammerhead"
-            }
-          ],
-          "hard_timeout": 180,
-          "output_links": [
-            {
-              "link": [
-                "https://luci-logdog.appspot.com/v/?s",
-                "=android%2Fswarming%2Flogcats%2F",
-                "${TASK_ID}%2F%2B%2Funified_logcats"
-              ],
-              "name": "shard #${SHARD_INDEX} logcats"
-            }
-          ]
-        },
-        "test": "mojo_system_unittests"
-      },
-      {
-        "args": [
-          "--gs-results-bucket=chromium-result-details",
-          "--recover-devices"
-        ],
-        "merge": {
-          "args": [
-            "--bucket",
-            "chromium-result-details",
-            "--test-name",
             "mojo_test_apk"
           ],
           "script": "//build/android/pylib/results/presentation/test_results_presentation.py"
@@ -3440,6 +3225,49 @@
             "--bucket",
             "chromium-result-details",
             "--test-name",
+            "mojo_unittests"
+          ],
+          "script": "//build/android/pylib/results/presentation/test_results_presentation.py"
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "cipd_packages": [
+            {
+              "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
+              "location": "bin",
+              "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c"
+            }
+          ],
+          "dimension_sets": [
+            {
+              "device_os": "KTU84P",
+              "device_type": "hammerhead"
+            }
+          ],
+          "hard_timeout": 180,
+          "output_links": [
+            {
+              "link": [
+                "https://luci-logdog.appspot.com/v/?s",
+                "=android%2Fswarming%2Flogcats%2F",
+                "${TASK_ID}%2F%2B%2Funified_logcats"
+              ],
+              "name": "shard #${SHARD_INDEX} logcats"
+            }
+          ]
+        },
+        "test": "mojo_unittests"
+      },
+      {
+        "args": [
+          "--gs-results-bucket=chromium-result-details",
+          "--recover-devices"
+        ],
+        "merge": {
+          "args": [
+            "--bucket",
+            "chromium-result-details",
+            "--test-name",
             "net_unittests"
           ],
           "script": "//build/android/pylib/results/presentation/test_results_presentation.py"
@@ -10907,135 +10735,6 @@
             "--bucket",
             "chromium-result-details",
             "--test-name",
-            "mojo_public_bindings_unittests"
-          ],
-          "script": "//build/android/pylib/results/presentation/test_results_presentation.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "cipd_packages": [
-            {
-              "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
-              "location": "bin",
-              "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c"
-            }
-          ],
-          "dimension_sets": [
-            {
-              "device_os": "MMB29Q",
-              "device_type": "bullhead"
-            }
-          ],
-          "hard_timeout": 120,
-          "output_links": [
-            {
-              "link": [
-                "https://luci-logdog.appspot.com/v/?s",
-                "=android%2Fswarming%2Flogcats%2F",
-                "${TASK_ID}%2F%2B%2Funified_logcats"
-              ],
-              "name": "shard #${SHARD_INDEX} logcats"
-            }
-          ]
-        },
-        "test": "mojo_public_bindings_unittests"
-      },
-      {
-        "args": [
-          "--gs-results-bucket=chromium-result-details",
-          "--recover-devices"
-        ],
-        "merge": {
-          "args": [
-            "--bucket",
-            "chromium-result-details",
-            "--test-name",
-            "mojo_public_system_unittests"
-          ],
-          "script": "//build/android/pylib/results/presentation/test_results_presentation.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "cipd_packages": [
-            {
-              "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
-              "location": "bin",
-              "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c"
-            }
-          ],
-          "dimension_sets": [
-            {
-              "device_os": "MMB29Q",
-              "device_type": "bullhead"
-            }
-          ],
-          "hard_timeout": 120,
-          "output_links": [
-            {
-              "link": [
-                "https://luci-logdog.appspot.com/v/?s",
-                "=android%2Fswarming%2Flogcats%2F",
-                "${TASK_ID}%2F%2B%2Funified_logcats"
-              ],
-              "name": "shard #${SHARD_INDEX} logcats"
-            }
-          ]
-        },
-        "test": "mojo_public_system_unittests"
-      },
-      {
-        "args": [
-          "--gs-results-bucket=chromium-result-details",
-          "--recover-devices"
-        ],
-        "merge": {
-          "args": [
-            "--bucket",
-            "chromium-result-details",
-            "--test-name",
-            "mojo_system_unittests"
-          ],
-          "script": "//build/android/pylib/results/presentation/test_results_presentation.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "cipd_packages": [
-            {
-              "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
-              "location": "bin",
-              "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c"
-            }
-          ],
-          "dimension_sets": [
-            {
-              "device_os": "MMB29Q",
-              "device_type": "bullhead"
-            }
-          ],
-          "hard_timeout": 300,
-          "output_links": [
-            {
-              "link": [
-                "https://luci-logdog.appspot.com/v/?s",
-                "=android%2Fswarming%2Flogcats%2F",
-                "${TASK_ID}%2F%2B%2Funified_logcats"
-              ],
-              "name": "shard #${SHARD_INDEX} logcats"
-            }
-          ]
-        },
-        "test": "mojo_system_unittests"
-      },
-      {
-        "args": [
-          "--gs-results-bucket=chromium-result-details",
-          "--recover-devices"
-        ],
-        "merge": {
-          "args": [
-            "--bucket",
-            "chromium-result-details",
-            "--test-name",
             "mojo_test_apk"
           ],
           "script": "//build/android/pylib/results/presentation/test_results_presentation.py"
@@ -11079,6 +10778,49 @@
             "--bucket",
             "chromium-result-details",
             "--test-name",
+            "mojo_unittests"
+          ],
+          "script": "//build/android/pylib/results/presentation/test_results_presentation.py"
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "cipd_packages": [
+            {
+              "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
+              "location": "bin",
+              "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c"
+            }
+          ],
+          "dimension_sets": [
+            {
+              "device_os": "MMB29Q",
+              "device_type": "bullhead"
+            }
+          ],
+          "hard_timeout": 300,
+          "output_links": [
+            {
+              "link": [
+                "https://luci-logdog.appspot.com/v/?s",
+                "=android%2Fswarming%2Flogcats%2F",
+                "${TASK_ID}%2F%2B%2Funified_logcats"
+              ],
+              "name": "shard #${SHARD_INDEX} logcats"
+            }
+          ]
+        },
+        "test": "mojo_unittests"
+      },
+      {
+        "args": [
+          "--gs-results-bucket=chromium-result-details",
+          "--recover-devices"
+        ],
+        "merge": {
+          "args": [
+            "--bucket",
+            "chromium-result-details",
+            "--test-name",
             "net_unittests"
           ],
           "script": "//build/android/pylib/results/presentation/test_results_presentation.py"
diff --git a/testing/buildbot/chromium.chromiumos.json b/testing/buildbot/chromium.chromiumos.json
index a327bca..3fc2e22 100644
--- a/testing/buildbot/chromium.chromiumos.json
+++ b/testing/buildbot/chromium.chromiumos.json
@@ -841,7 +841,7 @@
       },
       {
         "args": [
-          "--mash",
+          "--enable-features=Mash",
           "--test-launcher-filter-file=../../testing/buildbot/filters/ash_unittests_mash.filter"
         ],
         "name": "mash_ash_unittests",
@@ -853,7 +853,7 @@
       },
       {
         "args": [
-          "--mus"
+          "--enable-features=Mus"
         ],
         "name": "mus_ash_unittests",
         "swarming": {
@@ -888,7 +888,7 @@
       },
       {
         "args": [
-          "--mash",
+          "--enable-features=Mash",
           "--override-use-software-gl-for-tests",
           "--test-launcher-filter-file=../../testing/buildbot/filters/mash.browser_tests.filter"
         ],
@@ -902,7 +902,7 @@
       },
       {
         "args": [
-          "--mus",
+          "--enable-features=Mus",
           "--ozone-platform=headless",
           "--override-use-software-gl-for-tests"
         ],
@@ -970,7 +970,7 @@
       },
       {
         "args": [
-          "--mus"
+          "--enable-features=Mus"
         ],
         "name": "mus_content_browsertests",
         "swarming": {
@@ -999,7 +999,7 @@
       },
       {
         "args": [
-          "--mus"
+          "--enable-features=Mus"
         ],
         "name": "mus_content_unittests",
         "swarming": {
@@ -1309,7 +1309,7 @@
       },
       {
         "args": [
-          "--mus"
+          "--enable-features=Mus"
         ],
         "name": "mus_unit_tests",
         "swarming": {
diff --git a/testing/buildbot/chromium.clang.json b/testing/buildbot/chromium.clang.json
index 6aa7fbb..2082b16 100644
--- a/testing/buildbot/chromium.clang.json
+++ b/testing/buildbot/chromium.clang.json
@@ -266,19 +266,7 @@
         "swarming": {
           "can_use_on_swarming_builders": true
         },
-        "test": "mojo_public_bindings_unittests"
-      },
-      {
-        "swarming": {
-          "can_use_on_swarming_builders": true
-        },
-        "test": "mojo_public_system_unittests"
-      },
-      {
-        "swarming": {
-          "can_use_on_swarming_builders": true
-        },
-        "test": "mojo_system_unittests"
+        "test": "mojo_unittests"
       },
       {
         "swarming": {
@@ -661,19 +649,7 @@
         "swarming": {
           "can_use_on_swarming_builders": true
         },
-        "test": "mojo_public_bindings_unittests"
-      },
-      {
-        "swarming": {
-          "can_use_on_swarming_builders": true
-        },
-        "test": "mojo_public_system_unittests"
-      },
-      {
-        "swarming": {
-          "can_use_on_swarming_builders": true
-        },
-        "test": "mojo_system_unittests"
+        "test": "mojo_unittests"
       },
       {
         "swarming": {
@@ -5008,42 +4984,6 @@
             }
           ]
         },
-        "test": "mojo_public_bindings_unittests"
-      },
-      {
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "device_os": "MMB29Q",
-              "device_type": "bullhead"
-            }
-          ]
-        },
-        "test": "mojo_public_system_unittests"
-      },
-      {
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "device_os": "MMB29Q",
-              "device_type": "bullhead"
-            }
-          ]
-        },
-        "test": "mojo_system_unittests"
-      },
-      {
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "device_os": "MMB29Q",
-              "device_type": "bullhead"
-            }
-          ]
-        },
         "test": "mojo_test_apk"
       },
       {
@@ -5054,6 +4994,18 @@
               "device_os": "MMB29Q",
               "device_type": "bullhead"
             }
+          ]
+        },
+        "test": "mojo_unittests"
+      },
+      {
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "device_os": "MMB29Q",
+              "device_type": "bullhead"
+            }
           ],
           "shards": 4
         },
@@ -5472,19 +5424,7 @@
         "swarming": {
           "can_use_on_swarming_builders": true
         },
-        "test": "mojo_public_bindings_unittests"
-      },
-      {
-        "swarming": {
-          "can_use_on_swarming_builders": true
-        },
-        "test": "mojo_public_system_unittests"
-      },
-      {
-        "swarming": {
-          "can_use_on_swarming_builders": true
-        },
-        "test": "mojo_system_unittests"
+        "test": "mojo_unittests"
       },
       {
         "swarming": {
@@ -6098,19 +6038,7 @@
         "swarming": {
           "can_use_on_swarming_builders": true
         },
-        "test": "mojo_public_bindings_unittests"
-      },
-      {
-        "swarming": {
-          "can_use_on_swarming_builders": true
-        },
-        "test": "mojo_public_system_unittests"
-      },
-      {
-        "swarming": {
-          "can_use_on_swarming_builders": true
-        },
-        "test": "mojo_system_unittests"
+        "test": "mojo_unittests"
       },
       {
         "swarming": {
@@ -6465,19 +6393,7 @@
         "swarming": {
           "can_use_on_swarming_builders": false
         },
-        "test": "mojo_public_bindings_unittests"
-      },
-      {
-        "swarming": {
-          "can_use_on_swarming_builders": false
-        },
-        "test": "mojo_public_system_unittests"
-      },
-      {
-        "swarming": {
-          "can_use_on_swarming_builders": false
-        },
-        "test": "mojo_system_unittests"
+        "test": "mojo_unittests"
       },
       {
         "swarming": {
@@ -7019,19 +6935,7 @@
         "swarming": {
           "can_use_on_swarming_builders": true
         },
-        "test": "mojo_public_bindings_unittests"
-      },
-      {
-        "swarming": {
-          "can_use_on_swarming_builders": true
-        },
-        "test": "mojo_public_system_unittests"
-      },
-      {
-        "swarming": {
-          "can_use_on_swarming_builders": true
-        },
-        "test": "mojo_system_unittests"
+        "test": "mojo_unittests"
       },
       {
         "swarming": {
@@ -7371,19 +7275,7 @@
         "swarming": {
           "can_use_on_swarming_builders": true
         },
-        "test": "mojo_public_bindings_unittests"
-      },
-      {
-        "swarming": {
-          "can_use_on_swarming_builders": true
-        },
-        "test": "mojo_public_system_unittests"
-      },
-      {
-        "swarming": {
-          "can_use_on_swarming_builders": true
-        },
-        "test": "mojo_system_unittests"
+        "test": "mojo_unittests"
       },
       {
         "swarming": {
diff --git a/testing/buildbot/chromium.fyi.json b/testing/buildbot/chromium.fyi.json
index a6166781..f2bf6a8 100644
--- a/testing/buildbot/chromium.fyi.json
+++ b/testing/buildbot/chromium.fyi.json
@@ -579,19 +579,7 @@
         "swarming": {
           "can_use_on_swarming_builders": true
         },
-        "test": "mojo_public_bindings_unittests"
-      },
-      {
-        "swarming": {
-          "can_use_on_swarming_builders": true
-        },
-        "test": "mojo_public_system_unittests"
-      },
-      {
-        "swarming": {
-          "can_use_on_swarming_builders": true
-        },
-        "test": "mojo_system_unittests"
+        "test": "mojo_unittests"
       },
       {
         "swarming": {
@@ -974,19 +962,7 @@
         "swarming": {
           "can_use_on_swarming_builders": true
         },
-        "test": "mojo_public_bindings_unittests"
-      },
-      {
-        "swarming": {
-          "can_use_on_swarming_builders": true
-        },
-        "test": "mojo_public_system_unittests"
-      },
-      {
-        "swarming": {
-          "can_use_on_swarming_builders": true
-        },
-        "test": "mojo_system_unittests"
+        "test": "mojo_unittests"
       },
       {
         "swarming": {
@@ -1331,19 +1307,7 @@
         "swarming": {
           "can_use_on_swarming_builders": false
         },
-        "test": "mojo_public_bindings_unittests"
-      },
-      {
-        "swarming": {
-          "can_use_on_swarming_builders": false
-        },
-        "test": "mojo_public_system_unittests"
-      },
-      {
-        "swarming": {
-          "can_use_on_swarming_builders": false
-        },
-        "test": "mojo_system_unittests"
+        "test": "mojo_unittests"
       },
       {
         "swarming": {
@@ -1682,19 +1646,7 @@
         "swarming": {
           "can_use_on_swarming_builders": false
         },
-        "test": "mojo_public_bindings_unittests"
-      },
-      {
-        "swarming": {
-          "can_use_on_swarming_builders": false
-        },
-        "test": "mojo_public_system_unittests"
-      },
-      {
-        "swarming": {
-          "can_use_on_swarming_builders": false
-        },
-        "test": "mojo_system_unittests"
+        "test": "mojo_unittests"
       },
       {
         "swarming": {
@@ -2077,19 +2029,7 @@
         "swarming": {
           "can_use_on_swarming_builders": false
         },
-        "test": "mojo_public_bindings_unittests"
-      },
-      {
-        "swarming": {
-          "can_use_on_swarming_builders": false
-        },
-        "test": "mojo_public_system_unittests"
-      },
-      {
-        "swarming": {
-          "can_use_on_swarming_builders": false
-        },
-        "test": "mojo_system_unittests"
+        "test": "mojo_unittests"
       },
       {
         "swarming": {
@@ -2434,19 +2374,7 @@
         "swarming": {
           "can_use_on_swarming_builders": true
         },
-        "test": "mojo_public_bindings_unittests"
-      },
-      {
-        "swarming": {
-          "can_use_on_swarming_builders": true
-        },
-        "test": "mojo_public_system_unittests"
-      },
-      {
-        "swarming": {
-          "can_use_on_swarming_builders": true
-        },
-        "test": "mojo_system_unittests"
+        "test": "mojo_unittests"
       },
       {
         "swarming": {
@@ -2932,19 +2860,7 @@
         "swarming": {
           "can_use_on_swarming_builders": true
         },
-        "test": "mojo_public_bindings_unittests"
-      },
-      {
-        "swarming": {
-          "can_use_on_swarming_builders": true
-        },
-        "test": "mojo_public_system_unittests"
-      },
-      {
-        "swarming": {
-          "can_use_on_swarming_builders": true
-        },
-        "test": "mojo_system_unittests"
+        "test": "mojo_unittests"
       },
       {
         "swarming": {
@@ -3999,30 +3915,8 @@
         "test": "mojo_common_unittests"
       },
       {
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "kvm": "1"
-            }
-          ]
-        },
-        "test": "mojo_public_bindings_unittests"
-      },
-      {
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "kvm": "1"
-            }
-          ]
-        },
-        "test": "mojo_public_system_unittests"
-      },
-      {
         "args": [
-          "--test-launcher-filter-file=../../testing/buildbot/filters/fuchsia.mojo_system_unittests.filter"
+          "--test-launcher-filter-file=../../testing/buildbot/filters/fuchsia.mojo_unittests.filter"
         ],
         "swarming": {
           "can_use_on_swarming_builders": true,
@@ -4032,7 +3926,7 @@
             }
           ]
         },
-        "test": "mojo_system_unittests"
+        "test": "mojo_unittests"
       },
       {
         "args": [
@@ -4176,30 +4070,8 @@
         "test": "mojo_common_unittests"
       },
       {
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "kvm": "1"
-            }
-          ]
-        },
-        "test": "mojo_public_bindings_unittests"
-      },
-      {
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "kvm": "1"
-            }
-          ]
-        },
-        "test": "mojo_public_system_unittests"
-      },
-      {
         "args": [
-          "--test-launcher-filter-file=../../testing/buildbot/filters/fuchsia.mojo_system_unittests.filter"
+          "--test-launcher-filter-file=../../testing/buildbot/filters/fuchsia.mojo_unittests.filter"
         ],
         "swarming": {
           "can_use_on_swarming_builders": true,
@@ -4209,7 +4081,7 @@
             }
           ]
         },
-        "test": "mojo_system_unittests"
+        "test": "mojo_unittests"
       },
       {
         "args": [
@@ -4299,25 +4171,13 @@
         "test": "mojo_common_unittests"
       },
       {
-        "swarming": {
-          "can_use_on_swarming_builders": true
-        },
-        "test": "mojo_public_bindings_unittests"
-      },
-      {
-        "swarming": {
-          "can_use_on_swarming_builders": true
-        },
-        "test": "mojo_public_system_unittests"
-      },
-      {
         "args": [
-          "--test-launcher-filter-file=../../testing/buildbot/filters/fuchsia.mojo_system_unittests.filter"
+          "--test-launcher-filter-file=../../testing/buildbot/filters/fuchsia.mojo_unittests.filter"
         ],
         "swarming": {
           "can_use_on_swarming_builders": true
         },
-        "test": "mojo_system_unittests"
+        "test": "mojo_unittests"
       },
       {
         "args": [
@@ -5319,29 +5179,7 @@
             }
           ]
         },
-        "test": "mojo_public_bindings_unittests"
-      },
-      {
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "os": "Ubuntu-16.04"
-            }
-          ]
-        },
-        "test": "mojo_public_system_unittests"
-      },
-      {
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "os": "Ubuntu-16.04"
-            }
-          ]
-        },
-        "test": "mojo_system_unittests"
+        "test": "mojo_unittests"
       },
       {
         "swarming": {
@@ -6060,7 +5898,7 @@
     "gtest_tests": [
       {
         "args": [
-          "--mash",
+          "--enable-features=Mash",
           "--test-launcher-filter-file=../../testing/buildbot/filters/ash_unittests_mash.filter"
         ],
         "name": "mash_ash_unittests",
@@ -6072,7 +5910,7 @@
       },
       {
         "args": [
-          "--mus"
+          "--enable-features=Mus"
         ],
         "name": "mus_ash_unittests",
         "swarming": {
@@ -6082,7 +5920,7 @@
       },
       {
         "args": [
-          "--mash",
+          "--enable-features=Mash",
           "--override-use-software-gl-for-tests",
           "--test-launcher-filter-file=../../testing/buildbot/filters/mojo.fyi.mash.browser_tests.filter"
         ],
@@ -6096,7 +5934,7 @@
       },
       {
         "args": [
-          "--mus",
+          "--enable-features=Mus",
           "--ozone-platform=headless",
           "--override-use-software-gl-for-tests"
         ],
@@ -6132,7 +5970,7 @@
       },
       {
         "args": [
-          "--mus"
+          "--enable-features=Mus"
         ],
         "name": "mus_content_browsertests",
         "swarming": {
@@ -6166,7 +6004,7 @@
       },
       {
         "args": [
-          "--mus"
+          "--enable-features=Mus"
         ],
         "name": "mus_content_unittests",
         "swarming": {
@@ -6193,7 +6031,7 @@
       },
       {
         "args": [
-          "--mus"
+          "--enable-features=Mus"
         ],
         "name": "mus_unit_tests",
         "swarming": {
@@ -6727,29 +6565,7 @@
             }
           ]
         },
-        "test": "mojo_public_bindings_unittests"
-      },
-      {
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "device_type": "coho"
-            }
-          ]
-        },
-        "test": "mojo_public_system_unittests"
-      },
-      {
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "device_type": "coho"
-            }
-          ]
-        },
-        "test": "mojo_system_unittests"
+        "test": "mojo_unittests"
       },
       {
         "swarming": {
@@ -7126,19 +6942,7 @@
         "swarming": {
           "can_use_on_swarming_builders": true
         },
-        "test": "mojo_public_bindings_unittests"
-      },
-      {
-        "swarming": {
-          "can_use_on_swarming_builders": true
-        },
-        "test": "mojo_public_system_unittests"
-      },
-      {
-        "swarming": {
-          "can_use_on_swarming_builders": true
-        },
-        "test": "mojo_system_unittests"
+        "test": "mojo_unittests"
       },
       {
         "swarming": {
@@ -7513,19 +7317,7 @@
         "swarming": {
           "can_use_on_swarming_builders": true
         },
-        "test": "mojo_public_bindings_unittests"
-      },
-      {
-        "swarming": {
-          "can_use_on_swarming_builders": true
-        },
-        "test": "mojo_public_system_unittests"
-      },
-      {
-        "swarming": {
-          "can_use_on_swarming_builders": true
-        },
-        "test": "mojo_system_unittests"
+        "test": "mojo_unittests"
       },
       {
         "swarming": {
@@ -8463,19 +8255,7 @@
         "swarming": {
           "can_use_on_swarming_builders": false
         },
-        "test": "mojo_public_bindings_unittests"
-      },
-      {
-        "swarming": {
-          "can_use_on_swarming_builders": false
-        },
-        "test": "mojo_public_system_unittests"
-      },
-      {
-        "swarming": {
-          "can_use_on_swarming_builders": false
-        },
-        "test": "mojo_system_unittests"
+        "test": "mojo_unittests"
       },
       {
         "swarming": {
diff --git a/testing/buildbot/chromium.linux.json b/testing/buildbot/chromium.linux.json
index 384060c..831b1ec 100644
--- a/testing/buildbot/chromium.linux.json
+++ b/testing/buildbot/chromium.linux.json
@@ -466,6 +466,9 @@
         "test": "mojo_common_unittests"
       },
       {
+        "args": [
+          "--test-launcher-filter-file=../../testing/buildbot/filters/fuchsia.mojo_unittests.filter"
+        ],
         "swarming": {
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
@@ -474,18 +477,7 @@
             }
           ]
         },
-        "test": "mojo_public_bindings_unittests"
-      },
-      {
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "kvm": "1"
-            }
-          ]
-        },
-        "test": "mojo_public_system_unittests"
+        "test": "mojo_unittests"
       },
       {
         "args": [
@@ -1016,19 +1008,7 @@
         "swarming": {
           "can_use_on_swarming_builders": true
         },
-        "test": "mojo_public_bindings_unittests"
-      },
-      {
-        "swarming": {
-          "can_use_on_swarming_builders": true
-        },
-        "test": "mojo_public_system_unittests"
-      },
-      {
-        "swarming": {
-          "can_use_on_swarming_builders": true
-        },
-        "test": "mojo_system_unittests"
+        "test": "mojo_unittests"
       },
       {
         "swarming": {
@@ -1786,19 +1766,7 @@
         "swarming": {
           "can_use_on_swarming_builders": true
         },
-        "test": "mojo_public_bindings_unittests"
-      },
-      {
-        "swarming": {
-          "can_use_on_swarming_builders": true
-        },
-        "test": "mojo_public_system_unittests"
-      },
-      {
-        "swarming": {
-          "can_use_on_swarming_builders": true
-        },
-        "test": "mojo_system_unittests"
+        "test": "mojo_unittests"
       },
       {
         "swarming": {
@@ -2329,19 +2297,7 @@
         "swarming": {
           "can_use_on_swarming_builders": true
         },
-        "test": "mojo_public_bindings_unittests"
-      },
-      {
-        "swarming": {
-          "can_use_on_swarming_builders": true
-        },
-        "test": "mojo_public_system_unittests"
-      },
-      {
-        "swarming": {
-          "can_use_on_swarming_builders": true
-        },
-        "test": "mojo_system_unittests"
+        "test": "mojo_unittests"
       },
       {
         "swarming": {
diff --git a/testing/buildbot/chromium.mac.json b/testing/buildbot/chromium.mac.json
index 9406051..b1e3382 100644
--- a/testing/buildbot/chromium.mac.json
+++ b/testing/buildbot/chromium.mac.json
@@ -253,19 +253,7 @@
         "swarming": {
           "can_use_on_swarming_builders": true
         },
-        "test": "mojo_public_bindings_unittests"
-      },
-      {
-        "swarming": {
-          "can_use_on_swarming_builders": true
-        },
-        "test": "mojo_public_system_unittests"
-      },
-      {
-        "swarming": {
-          "can_use_on_swarming_builders": true
-        },
-        "test": "mojo_system_unittests"
+        "test": "mojo_unittests"
       },
       {
         "swarming": {
@@ -707,19 +695,7 @@
         "swarming": {
           "can_use_on_swarming_builders": true
         },
-        "test": "mojo_public_bindings_unittests"
-      },
-      {
-        "swarming": {
-          "can_use_on_swarming_builders": true
-        },
-        "test": "mojo_public_system_unittests"
-      },
-      {
-        "swarming": {
-          "can_use_on_swarming_builders": true
-        },
-        "test": "mojo_system_unittests"
+        "test": "mojo_unittests"
       },
       {
         "swarming": {
@@ -1161,19 +1137,7 @@
         "swarming": {
           "can_use_on_swarming_builders": true
         },
-        "test": "mojo_public_bindings_unittests"
-      },
-      {
-        "swarming": {
-          "can_use_on_swarming_builders": true
-        },
-        "test": "mojo_public_system_unittests"
-      },
-      {
-        "swarming": {
-          "can_use_on_swarming_builders": true
-        },
-        "test": "mojo_system_unittests"
+        "test": "mojo_unittests"
       },
       {
         "swarming": {
@@ -2317,53 +2281,7 @@
             }
           ]
         },
-        "test": "mojo_public_bindings_unittests",
-        "trigger_script": {
-          "args": [
-            "--multiple-trigger-configs",
-            "[{\"gpu\": \"none\", \"os\": \"Mac-10.9\", \"cpu\": \"x86-64\", \"pool\": \"Chrome\"}, {\"gpu\": \"8086:0a2e\", \"os\": \"Mac-10.13\", \"cpu\": \"x86-64\", \"pool\": \"Chrome\"}]",
-            "--multiple-dimension-script-verbose",
-            "True"
-          ],
-          "script": "//testing/trigger_scripts/trigger_multiple_dimensions.py"
-        }
-      },
-      {
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "cpu": "x86-64",
-              "gpu": "none",
-              "os": "Mac-10.9",
-              "pool": "Chrome"
-            }
-          ]
-        },
-        "test": "mojo_public_system_unittests",
-        "trigger_script": {
-          "args": [
-            "--multiple-trigger-configs",
-            "[{\"gpu\": \"none\", \"os\": \"Mac-10.9\", \"cpu\": \"x86-64\", \"pool\": \"Chrome\"}, {\"gpu\": \"8086:0a2e\", \"os\": \"Mac-10.13\", \"cpu\": \"x86-64\", \"pool\": \"Chrome\"}]",
-            "--multiple-dimension-script-verbose",
-            "True"
-          ],
-          "script": "//testing/trigger_scripts/trigger_multiple_dimensions.py"
-        }
-      },
-      {
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "cpu": "x86-64",
-              "gpu": "none",
-              "os": "Mac-10.9",
-              "pool": "Chrome"
-            }
-          ]
-        },
-        "test": "mojo_system_unittests",
+        "test": "mojo_unittests",
         "trigger_script": {
           "args": [
             "--multiple-trigger-configs",
@@ -3290,19 +3208,7 @@
         "swarming": {
           "can_use_on_swarming_builders": true
         },
-        "test": "mojo_public_bindings_unittests"
-      },
-      {
-        "swarming": {
-          "can_use_on_swarming_builders": true
-        },
-        "test": "mojo_public_system_unittests"
-      },
-      {
-        "swarming": {
-          "can_use_on_swarming_builders": true
-        },
-        "test": "mojo_system_unittests"
+        "test": "mojo_unittests"
       },
       {
         "swarming": {
diff --git a/testing/buildbot/chromium.memory.json b/testing/buildbot/chromium.memory.json
index 92079704..2ea9156 100644
--- a/testing/buildbot/chromium.memory.json
+++ b/testing/buildbot/chromium.memory.json
@@ -752,19 +752,7 @@
         "swarming": {
           "can_use_on_swarming_builders": true
         },
-        "test": "mojo_public_bindings_unittests"
-      },
-      {
-        "swarming": {
-          "can_use_on_swarming_builders": true
-        },
-        "test": "mojo_public_system_unittests"
-      },
-      {
-        "swarming": {
-          "can_use_on_swarming_builders": true
-        },
-        "test": "mojo_system_unittests"
+        "test": "mojo_unittests"
       },
       {
         "swarming": {
@@ -916,7 +904,7 @@
       },
       {
         "args": [
-          "--mus"
+          "--enable-features=Mus"
         ],
         "name": "mus_ash_unittests",
         "swarming": {
@@ -948,7 +936,7 @@
       },
       {
         "args": [
-          "--mus",
+          "--enable-features=Mus",
           "--ozone-platform=headless",
           "--override-use-software-gl-for-tests"
         ],
@@ -1009,7 +997,7 @@
       },
       {
         "args": [
-          "--mus"
+          "--enable-features=Mus"
         ],
         "name": "mus_content_browsertests",
         "swarming": {
@@ -1026,7 +1014,7 @@
       },
       {
         "args": [
-          "--mus"
+          "--enable-features=Mus"
         ],
         "name": "mus_content_unittests",
         "swarming": {
@@ -1251,7 +1239,7 @@
       },
       {
         "args": [
-          "--mus"
+          "--enable-features=Mus"
         ],
         "name": "mus_unit_tests",
         "swarming": {
@@ -1479,19 +1467,7 @@
         "swarming": {
           "can_use_on_swarming_builders": true
         },
-        "test": "mojo_public_bindings_unittests"
-      },
-      {
-        "swarming": {
-          "can_use_on_swarming_builders": true
-        },
-        "test": "mojo_public_system_unittests"
-      },
-      {
-        "swarming": {
-          "can_use_on_swarming_builders": true
-        },
-        "test": "mojo_system_unittests"
+        "test": "mojo_unittests"
       },
       {
         "swarming": {
@@ -1808,19 +1784,7 @@
         "swarming": {
           "can_use_on_swarming_builders": true
         },
-        "test": "mojo_public_bindings_unittests"
-      },
-      {
-        "swarming": {
-          "can_use_on_swarming_builders": true
-        },
-        "test": "mojo_public_system_unittests"
-      },
-      {
-        "swarming": {
-          "can_use_on_swarming_builders": true
-        },
-        "test": "mojo_system_unittests"
+        "test": "mojo_unittests"
       },
       {
         "swarming": {
diff --git a/testing/buildbot/chromium.perf.fyi.json b/testing/buildbot/chromium.perf.fyi.json
index 2924fa33..f480203 100644
--- a/testing/buildbot/chromium.perf.fyi.json
+++ b/testing/buildbot/chromium.perf.fyi.json
@@ -439,7 +439,8 @@
         "args": [
           "-v",
           "--xvfb",
-          "--browser=reference"
+          "--browser=reference",
+          "--testing=True"
         ],
         "isolate_name": "performance_test_suite",
         "merge": {
diff --git a/testing/buildbot/chromium.sandbox.json b/testing/buildbot/chromium.sandbox.json
index 6c7867a3..b9b08a0 100644
--- a/testing/buildbot/chromium.sandbox.json
+++ b/testing/buildbot/chromium.sandbox.json
@@ -11,9 +11,7 @@
       "ipc_tests",
       "media_unittests",
       "mojo_common_unittests",
-      "mojo_public_bindings_unittests",
-      "mojo_public_system_unittests",
-      "mojo_system_unittests",
+      "mojo_unittests",
       "net_unittests",
       "skia_unittests",
       "service_manager_unittests",
@@ -399,19 +397,7 @@
         "swarming": {
           "can_use_on_swarming_builders": true
         },
-        "test": "mojo_public_bindings_unittests"
-      },
-      {
-        "swarming": {
-          "can_use_on_swarming_builders": true
-        },
-        "test": "mojo_public_system_unittests"
-      },
-      {
-        "swarming": {
-          "can_use_on_swarming_builders": true
-        },
-        "test": "mojo_system_unittests"
+        "test": "mojo_unittests"
       },
       {
         "swarming": {
diff --git a/testing/buildbot/chromium.win.json b/testing/buildbot/chromium.win.json
index 43b21af5..7c3b0f1 100644
--- a/testing/buildbot/chromium.win.json
+++ b/testing/buildbot/chromium.win.json
@@ -349,19 +349,7 @@
         "swarming": {
           "can_use_on_swarming_builders": true
         },
-        "test": "mojo_public_bindings_unittests"
-      },
-      {
-        "swarming": {
-          "can_use_on_swarming_builders": true
-        },
-        "test": "mojo_public_system_unittests"
-      },
-      {
-        "swarming": {
-          "can_use_on_swarming_builders": true
-        },
-        "test": "mojo_system_unittests"
+        "test": "mojo_unittests"
       },
       {
         "swarming": {
@@ -956,19 +944,7 @@
         "swarming": {
           "can_use_on_swarming_builders": true
         },
-        "test": "mojo_public_bindings_unittests"
-      },
-      {
-        "swarming": {
-          "can_use_on_swarming_builders": true
-        },
-        "test": "mojo_public_system_unittests"
-      },
-      {
-        "swarming": {
-          "can_use_on_swarming_builders": true
-        },
-        "test": "mojo_system_unittests"
+        "test": "mojo_unittests"
       },
       {
         "swarming": {
@@ -1529,19 +1505,7 @@
         "swarming": {
           "can_use_on_swarming_builders": true
         },
-        "test": "mojo_public_bindings_unittests"
-      },
-      {
-        "swarming": {
-          "can_use_on_swarming_builders": true
-        },
-        "test": "mojo_public_system_unittests"
-      },
-      {
-        "swarming": {
-          "can_use_on_swarming_builders": true
-        },
-        "test": "mojo_system_unittests"
+        "test": "mojo_unittests"
       },
       {
         "swarming": {
@@ -2103,19 +2067,7 @@
         "swarming": {
           "can_use_on_swarming_builders": true
         },
-        "test": "mojo_public_bindings_unittests"
-      },
-      {
-        "swarming": {
-          "can_use_on_swarming_builders": true
-        },
-        "test": "mojo_public_system_unittests"
-      },
-      {
-        "swarming": {
-          "can_use_on_swarming_builders": true
-        },
-        "test": "mojo_system_unittests"
+        "test": "mojo_unittests"
       },
       {
         "swarming": {
diff --git a/testing/buildbot/client.v8.fyi.json b/testing/buildbot/client.v8.fyi.json
index 156e40e..94cf860 100644
--- a/testing/buildbot/client.v8.fyi.json
+++ b/testing/buildbot/client.v8.fyi.json
@@ -1131,9 +1131,7 @@
       "media_unittests",
       "media_blink_unittests",
       "mojo_common_unittests",
-      "mojo_public_bindings_unittests",
-      "mojo_public_system_unittests",
-      "mojo_system_unittests",
+      "mojo_unittests",
       "nacl_loader_unittests",
       "net_unittests",
       "pdf_unittests",
diff --git a/testing/buildbot/filters/BUILD.gn b/testing/buildbot/filters/BUILD.gn
index 7dc3897..207a1981 100644
--- a/testing/buildbot/filters/BUILD.gn
+++ b/testing/buildbot/filters/BUILD.gn
@@ -86,7 +86,7 @@
     "//testing/buildbot/filters/fuchsia.base_unittests.filter",
     "//testing/buildbot/filters/fuchsia.content_unittests.filter",
     "//testing/buildbot/filters/fuchsia.ipc_tests.filter",
-    "//testing/buildbot/filters/fuchsia.mojo_system_unittests.filter",
+    "//testing/buildbot/filters/fuchsia.mojo_unittests.filter",
     "//testing/buildbot/filters/fuchsia.net_unittests.filter",
     "//testing/buildbot/filters/fuchsia.service_manager_unittests.filter",
     "//testing/buildbot/filters/fuchsia.ui_base_unittests.filter",
diff --git a/testing/buildbot/filters/fuchsia.base_unittests.filter b/testing/buildbot/filters/fuchsia.base_unittests.filter
index 4e6c464..f511a0e 100644
--- a/testing/buildbot/filters/fuchsia.base_unittests.filter
+++ b/testing/buildbot/filters/fuchsia.base_unittests.filter
@@ -74,6 +74,10 @@
 
 # Flaky, https://crbug.com/768436, https://crbug.com/792310.
 -TaskSchedulerWorkerPoolBlockingTest.MaximumWorkersTest
+-TaskSchedulerWorkerPoolBlockingTest.MayBlockIncreaseCapacityNestedWillBlock
+-TaskSchedulerWorkerPoolBlockingTest.PostBeforeBlocking
+-TaskSchedulerWorkerPoolBlockingTest.ThreadBlockedUnblocked
+-TaskSchedulerWorkerPoolBlockingTest.WorkersIdleWhenOverCapacity
 
 # Flaky, https://crbug.com/810077.
 -MessageLoopTypedTest.RecursivePosts/*
diff --git a/testing/buildbot/filters/fuchsia.mojo_system_unittests.filter b/testing/buildbot/filters/fuchsia.mojo_system_unittests.filter
index 61887048..0decdcd 100644
--- a/testing/buildbot/filters/fuchsia.mojo_system_unittests.filter
+++ b/testing/buildbot/filters/fuchsia.mojo_system_unittests.filter
@@ -14,3 +14,9 @@
 
 # Flaky, https://crbug.com/810448.
 -MultiprocessMessagePipeTestWithPeerSupport*/1
+
+# Flaky, https://crbug.com/814596.
+-MultiprocessMessagePipeTest.MoreChildToChildPipes
+
+# Flaky, https://crbug.com/755848.
+-EmbedderTest.MultiprocessBaseSharedMemory
diff --git a/testing/buildbot/filters/fuchsia.mojo_unittests.filter b/testing/buildbot/filters/fuchsia.mojo_unittests.filter
new file mode 100644
index 0000000..61887048
--- /dev/null
+++ b/testing/buildbot/filters/fuchsia.mojo_unittests.filter
@@ -0,0 +1,16 @@
+# TODO(fuchsia): Fix the tests and delete this file. crbug.com/740791
+
+# These tests are too slow under QEMU w/out KVM. See crbug.com/745801.
+-MessagePipeTest.ClosePipesStressTest
+-MessageTest.ExtendMessagePayloadLarge
+
+# These tests require support for named channels. See crbug.com/754038.
+-MultiprocessMessagePipeTestWithPeerSupport*/2
+-MultiprocessMessagePipeTestWithPeerSupport*/3
+
+# crbug.com/780317 - These timeout under QEMU s/w emulation of ARM64.
+-MultiprocessMessagePipeTestWithPeerSupport.PingPongPipe*
+-MessagePipeTest.SharedBufferHandlePingPong
+
+# Flaky, https://crbug.com/810448.
+-MultiprocessMessagePipeTestWithPeerSupport*/1
diff --git a/testing/buildbot/filters/fuchsia.net_unittests.filter b/testing/buildbot/filters/fuchsia.net_unittests.filter
index e468721..e41481ad 100644
--- a/testing/buildbot/filters/fuchsia.net_unittests.filter
+++ b/testing/buildbot/filters/fuchsia.net_unittests.filter
@@ -26,3 +26,6 @@
 
 # Flaky, https://crbug.com/813631.
 -URLRequestQuicTest.CancelPushIfCached_AllCached
+
+# Flaky, https://crbug.com/814811.
+-URLRequestTest.NetworkDelegateProxyError
diff --git a/testing/buildbot/filters/mash.browser_tests.filter b/testing/buildbot/filters/mash.browser_tests.filter
index 38495af..8d4f7a9 100644
--- a/testing/buildbot/filters/mash.browser_tests.filter
+++ b/testing/buildbot/filters/mash.browser_tests.filter
@@ -1,4 +1,4 @@
-# These tests fail when running browser_tests --mash
+# These tests fail when running browser_tests with mash
 # http://crbug.com/678687
 
 # Unknown failure.
@@ -245,10 +245,10 @@
 -VirtualKeyboardStateTest.*
 -VirtualKeyboardWebContentTest.*
 
-# Also fails in --mus. http://crbug.com/755318.
+# Also fails with mus. http://crbug.com/755318.
 -WebViewScrollBubbling/WebViewGuestScrollTouchTest.*
 
-# Also fails in --mus. http://crbug.com/755328
+# Also fails with mus. http://crbug.com/755328
 -WebViewTests/WebViewFocusTest.*
 -WebViewTests/WebViewSizeTest.*
 -WebViewTests/WebViewTest.*
@@ -268,3 +268,6 @@
 
 # HostedAppMenu needs porting to BrowserNonClientFrameViewMus crbug.com/813666
 -HostedAppPWAOnlyTest.AppInfoOpensPageInfo*
+
+# DCHECK in DelegatedFrameHost
+-SafeBrowsingTriggeredPopupBlockerBrowserTest.NoFeature_NoMessages
diff --git a/testing/buildbot/filters/mojo.fyi.mash.browser_tests.filter b/testing/buildbot/filters/mojo.fyi.mash.browser_tests.filter
index 38495af..932474f 100644
--- a/testing/buildbot/filters/mojo.fyi.mash.browser_tests.filter
+++ b/testing/buildbot/filters/mojo.fyi.mash.browser_tests.filter
@@ -1,4 +1,4 @@
-# These tests fail when running browser_tests --mash
+# These tests fail when running browser_tests with mash
 # http://crbug.com/678687
 
 # Unknown failure.
@@ -245,10 +245,10 @@
 -VirtualKeyboardStateTest.*
 -VirtualKeyboardWebContentTest.*
 
-# Also fails in --mus. http://crbug.com/755318.
+# Also fails with mus. http://crbug.com/755318.
 -WebViewScrollBubbling/WebViewGuestScrollTouchTest.*
 
-# Also fails in --mus. http://crbug.com/755328
+# Also fails with mus. http://crbug.com/755328
 -WebViewTests/WebViewFocusTest.*
 -WebViewTests/WebViewSizeTest.*
 -WebViewTests/WebViewTest.*
diff --git a/testing/buildbot/filters/mojo.fyi.network_browser_tests.filter b/testing/buildbot/filters/mojo.fyi.network_browser_tests.filter
index f53a2e8..4ffb677 100644
--- a/testing/buildbot/filters/mojo.fyi.network_browser_tests.filter
+++ b/testing/buildbot/filters/mojo.fyi.network_browser_tests.filter
@@ -2,7 +2,6 @@
 # See https://crbug.com/769401
 
 # Uncategorized timeouts or test failures.
--ActivityLogApiTest.TriggerEvent
 -AdsPageLoadMetricsObserverBrowserTest.DocOverwritesNavigation
 -AdsPageLoadMetricsObserverBrowserTest.SubresourceFilter
 -AppBackgroundPageNaClTest.BackgroundKeepaliveActive
@@ -262,8 +261,6 @@
 
 # http://crbug.com/721414
 # TODO(rockot): add support for webRequest API.
--DeclarativeNetRequestBrowserTest.BlockRequests_Incognito/0
--DeclarativeNetRequestBrowserTest.BlockRequests_Incognito/1
 -DeclarativeNetRequestBrowserTest.RendererCacheCleared/0
 -DeclarativeNetRequestBrowserTest.RendererCacheCleared/1
 -DevToolsFrontendInWebRequestApiTest.HiddenRequests
@@ -272,12 +269,8 @@
 -ExtensionWebRequestApiTest.WebRequestBlocking
 -ExtensionWebRequestApiTest.WebRequestClientsGoogleComProtection
 -ExtensionWebRequestApiTest.WebRequestDeclarative2
--ExtensionWebRequestApiTest.WebRequestDeclarativePermissionSpanning2
--ExtensionWebRequestApiTest.WebRequestDeclarativePermissionSplit1
--ExtensionWebRequestApiTest.WebRequestDeclarativePermissionSplit2
 -ExtensionWebRequestApiTest.WebRequestDiceHeaderProtection
 -ExtensionWebRequestApiTest.WebRequestTypes
--ExtensionWebRequestApiTest.WebRequestUnloadImmediately
 -ExtensionWebRequestApiTest.WebRequestWithWithheldPermissions
 -WebViewTests/WebViewTest.Shim_TestDeclarativeWebRequestAPI/0
 -WebViewTests/WebViewTest.Shim_TestDeclarativeWebRequestAPI/1
@@ -412,6 +405,13 @@
 -ExtensionRequestLimitingThrottleBrowserTest.ThrottleRequest_RedirectCached
 -ExtensionRequestLimitingThrottleCommandLineBrowserTest.ThrottleRequestDisabled
 
+# Redirect responses using NavigationThrottle or by hooking
+# NavigationURLLoaderNetworkService::URLLoaderRequestController
+# instead of net::URLRequestInterceptor.
+-NewTabPageInterceptorTest.204Interception
+-NewTabPageInterceptorTest.404Interception
+-NewTabPageInterceptorTest.FailedRequestInterception
+
 # This requires that InterceptNetworkTransactions works
 -ErrorPageNavigationCorrectionsFailTest.StaleCacheStatusFailedCorrections
 
diff --git a/testing/buildbot/gn_isolate_map.pyl b/testing/buildbot/gn_isolate_map.pyl
index 58f5ec4..9ada229 100644
--- a/testing/buildbot/gn_isolate_map.pyl
+++ b/testing/buildbot/gn_isolate_map.pyl
@@ -802,16 +802,8 @@
     "label": "//mojo/common:mojo_common_unittests",
     "type": "console_test_launcher",
   },
-  "mojo_public_bindings_unittests": {
-    "label": "//mojo/edk/test:mojo_public_bindings_unittests",
-    "type": "console_test_launcher",
-  },
-  "mojo_public_system_unittests": {
-    "label": "//mojo/edk/test:mojo_public_system_unittests",
-    "type": "console_test_launcher",
-  },
-  "mojo_system_unittests": {
-    "label": "//mojo/edk/system:mojo_system_unittests",
+  "mojo_unittests": {
+    "label": "//mojo:mojo_unittests",
     "type": "console_test_launcher",
   },
   "mojo_test_apk": {
diff --git a/testing/buildbot/test_suite_exceptions.pyl b/testing/buildbot/test_suite_exceptions.pyl
index be5109eb..50e9350 100644
--- a/testing/buildbot/test_suite_exceptions.pyl
+++ b/testing/buildbot/test_suite_exceptions.pyl
@@ -2343,7 +2343,7 @@
       },
     },
   },
-  'mojo_public_bindings_unittests': {
+  'mojo_unittests': {
     'remove_from': [
       # On chromium.android, unclear why these aren't run on all bots.
       'KitKat Tablet Tester',
@@ -2377,97 +2377,6 @@
       # chromium.android
       'Marshmallow Phone Tester (rel)': {
         'swarming': {
-          'hard_timeout': 120,
-        },
-      },
-      # chromium.android.fyi
-      'x64 Device Tester': {
-        'swarming': {
-          'hard_timeout': 0,
-        },
-      },
-    },
-  },
-  'mojo_public_system_unittests': {
-    'remove_from': [
-      # On chromium.android, unclear why these aren't run on all bots.
-      'KitKat Tablet Tester',
-      'Lollipop Phone Tester',
-      'Lollipop Tablet Tester',
-      'Marshmallow 64 bit Tester',
-      'Marshmallow Tablet Tester',
-      'Nougat Phone Tester',
-      # chromium.android.fyi
-      'Jelly Bean Tester',
-      'Lollipop Consumer Tester',
-      'Lollipop Low-end Tester',
-      'Unswarmed N5 Tests Dummy Builder',
-      'Unswarmed N5X Tests Dummy Builder',
-      # chromium.chromiumos
-      'Linux ChromiumOS Tests (dbg)(1)',
-      'linux-chromeos-dbg',
-      'linux-chromeos-rel',
-      # chromium.clang
-      'ToTLinuxASan',
-      'ToTLinuxUBSanVptr',
-      # chromium.linux
-      'Cast Audio Linux',
-      'Cast Linux',
-      # chromium.memory
-      'Linux ASan LSan Tests (1)',
-      'Linux Chromium OS ASan LSan Tests (1)',
-      'Linux TSan Tests',
-    ],
-    'modifications': {
-      # chromium.android
-      'Marshmallow Phone Tester (rel)': {
-        'swarming': {
-          'hard_timeout': 120,
-        },
-      },
-      # chromium.android.fyi
-      'x64 Device Tester': {
-        'swarming': {
-          'hard_timeout': 0,
-        },
-      },
-    },
-  },
-  'mojo_system_unittests': {
-    'remove_from': [
-      # On chromium.android, unclear why these aren't run on all bots.
-      'KitKat Tablet Tester',
-      'Lollipop Phone Tester',
-      'Lollipop Tablet Tester',
-      'Marshmallow 64 bit Tester',
-      'Marshmallow Tablet Tester',
-      'Nougat Phone Tester',
-      # chromium.android.fyi
-      'Jelly Bean Tester',
-      'Lollipop Consumer Tester',
-      'Lollipop Low-end Tester',
-      'Unswarmed N5 Tests Dummy Builder',
-      'Unswarmed N5X Tests Dummy Builder',
-      # chromium.chromiumos
-      'Linux ChromiumOS Tests (dbg)(1)',
-      'linux-chromeos-dbg',
-      'linux-chromeos-rel',
-      # chromium.clang
-      'ToTLinuxASan',
-      'ToTLinuxUBSanVptr',
-      # chromium.linux
-      'Cast Audio Linux',
-      'Cast Linux',
-      'Fuchsia x64',
-      # chromium.memory
-      'Linux ASan LSan Tests (1)',
-      'Linux Chromium OS ASan LSan Tests (1)',
-      'Linux TSan Tests',
-    ],
-    'modifications': {
-      # chromium.android
-      'Marshmallow Phone Tester (rel)': {
-        'swarming': {
           'hard_timeout': 300,
         },
       },
diff --git a/testing/buildbot/test_suites.pyl b/testing/buildbot/test_suites.pyl
index eb48c65..9fbbf753 100644
--- a/testing/buildbot/test_suites.pyl
+++ b/testing/buildbot/test_suites.pyl
@@ -808,13 +808,9 @@
     },
     'mojo_common_unittests': {
     },
-    'mojo_public_bindings_unittests': {
-    },
-    'mojo_public_system_unittests': {
-    },
-    'mojo_system_unittests': {
+    'mojo_unittests': {
       'args': [
-        '--test-launcher-filter-file=../../testing/buildbot/filters/fuchsia.mojo_system_unittests.filter',
+        '--test-launcher-filter-file=../../testing/buildbot/filters/fuchsia.mojo_unittests.filter',
       ],
     },
     'net_unittests': {
@@ -985,14 +981,14 @@
       'test': 'ash_unittests',
       'override_isolate_target': 'ash_unittests',
       'args': [
-        '--mash',
+        '--enable-features=Mash',
         '--test-launcher-filter-file=../../testing/buildbot/filters/ash_unittests_mash.filter',
       ],
     },
     'mash_browser_tests': {
       'test': 'browser_tests',
       'args': [
-        '--mash',
+        '--enable-features=Mash',
         '--override-use-software-gl-for-tests',
       ],
       'swarming': {
@@ -1065,13 +1061,13 @@
   'linux_chromeos_rel_mus_specific_gtests': {
     'mus_ash_unittests': {
       'args': [
-        '--mus',
+        '--enable-features=Mus',
       ],
       'test': 'ash_unittests',
     },
     'mus_browser_tests': {
       'args': [
-        '--mus',
+        '--enable-features=Mus',
         '--ozone-platform=headless',
         '--override-use-software-gl-for-tests',
       ],
@@ -1082,13 +1078,13 @@
     },
     'mus_content_unittests': {
       'args': [
-        '--mus',
+        '--enable-features=Mus',
       ],
       'test': 'content_unittests',
     },
     'mus_content_browsertests': {
       'args': [
-        '--mus',
+        '--enable-features=Mus',
       ],
       'swarming': {
         'shards': 2,
@@ -1097,7 +1093,7 @@
     },
     'mus_unit_tests': {
       'args': [
-        '--mus',
+        '--enable-features=Mus',
       ],
       'test': 'unit_tests',
     },
@@ -1351,17 +1347,7 @@
         'hard_timeout': 60,
       },
     },
-    'mojo_public_bindings_unittests': {
-      'android_swarming': {
-        'hard_timeout': 60,
-      },
-    },
-    'mojo_public_system_unittests': {
-      'android_swarming': {
-        'hard_timeout': 60,
-      },
-    },
-    'mojo_system_unittests': {
+    'mojo_unittests': {
       'android_swarming': {
         'hard_timeout': 180,
       },
diff --git a/testing/libfuzzer/fuzzers/v8_fuzzer.cc b/testing/libfuzzer/fuzzers/v8_fuzzer.cc
index 69085158..3f95f34 100644
--- a/testing/libfuzzer/fuzzers/v8_fuzzer.cc
+++ b/testing/libfuzzer/fuzzers/v8_fuzzer.cc
@@ -46,11 +46,6 @@
  public:
   MockArrayBufferAllocator()
       : v8::ArrayBuffer::Allocator(), currently_allocated_(0) {}
-  void SetProtection(void* data,
-                     size_t length,
-                     Protection protection) override {
-    allocator_->SetProtection(data, length, protection);
-  }
 
   void* Allocate(size_t length) override {
     void* data = AllocateUninitialized(length);
@@ -73,33 +68,6 @@
     // be innacurate.
     free(ptr);
   }
-
-  void Free(void* data, size_t length, AllocationMode mode) override {
-    switch (mode) {
-      case AllocationMode::kNormal: {
-        // Free locks and unlocks for us.
-        Free(data, length);
-        return;
-      }
-      case AllocationMode::kReservation: {
-        lock_guard<mutex> mtx_locker(mtx_);
-        currently_allocated_ -= length;
-        allocator_->Free(data, length, mode);
-        return;
-      }
-      default:
-        NOTREACHED();
-    }
-  }
-
-  void* Reserve(size_t length) override {
-    lock_guard<mutex> mtx_locker(mtx_);
-    if (length + currently_allocated_ > kAllocationLimit) {
-      return nullptr;
-    }
-    currently_allocated_ += length;
-    return allocator_->Reserve(length);
-  }
 };
 
 void terminate_execution(v8::Isolate* isolate,
diff --git a/testing/scripts/run_performance_tests.py b/testing/scripts/run_performance_tests.py
index ed53157..f19dbb09 100755
--- a/testing/scripts/run_performance_tests.py
+++ b/testing/scripts/run_performance_tests.py
@@ -62,21 +62,21 @@
 CURRENT_DESKTOP_NUM_SHARDS = 5
 CURRENT_ANDROID_NUM_SHARDS = 21
 
-def get_sharding_map_path(total_shards):
-  """
-  UNCOMMENT WHEN WE HAVE PROVED THE BASE CASE IN benchmark_bot_map.json
-  # Note: <= for testing purposes until we have all shards running
-  if int(total_shards) <= CURRENT_DESKTOP_NUM_SHARDS:
-    return os.path.join(
-        os.path.dirname(__file__), '..', '..', 'tools', 'perf', 'core',
-        'benchmark_desktop_bot_map.json')
+def get_sharding_map_path(total_shards, testing):
+  # Determine if we want to do a test run of the benchmarks or run the
+  # full suite.
+  if not testing:
+    # Note: <= for testing purposes until we have all shards running
+    if int(total_shards) <= CURRENT_DESKTOP_NUM_SHARDS:
+      return os.path.join(
+          os.path.dirname(__file__), '..', '..', 'tools', 'perf', 'core',
+          'benchmark_desktop_bot_map.json')
+    else:
+      return os.path.join(
+          os.path.dirname(__file__), '..', '..', 'tools', 'perf', 'core',
+          'benchmark_android_bot_map.json')
   else:
     return os.path.join(
-        os.path.dirname(__file__), '..', '..', 'tools', 'perf', 'core',
-        'benchmark_android_bot_map.json')
-  """
-  del total_shards #unused
-  return os.path.join(
       os.path.dirname(__file__), '..', '..', 'tools', 'perf', 'core',
       'benchmark_bot_map.json')
 
@@ -110,6 +110,9 @@
         break
       browser_index = browser_index + 1
     per_benchmark_args[browser_index] = '--browser=reference'
+    # Now we need to add in the rest of the reference build args
+    per_benchmark_args.append('--max-failures=5')
+    per_benchmark_args.append('--output-trace-tag=_ref')
     benchmark_path = os.path.join(isolated_out_dir, benchmark + '.reference')
   else:
     benchmark_path = os.path.join(isolated_out_dir, benchmark)
@@ -145,6 +148,8 @@
   parser.add_argument(
       '--isolated-script-test-filter', type=str, required=False)
   parser.add_argument('--xvfb', help='Start xvfb.', action='store_true')
+  parser.add_argument('--testing', help='Testing instance',
+                      type=bool, default=False)
 
   args, rest_args = parser.parse_known_args()
   isolated_out_dir = os.path.dirname(args.isolated_script_test_output)
@@ -163,7 +168,7 @@
   if not (total_shards or shard_index):
     raise Exception('Shard indicators must be present for perf tests')
 
-  sharding_map_path = get_sharding_map_path(total_shards)
+  sharding_map_path = get_sharding_map_path(total_shards, args.testing or False)
   with open(sharding_map_path) as f:
     sharding_map = json.load(f)
   sharding = None
diff --git a/testing/variations/fieldtrial_testing_config.json b/testing/variations/fieldtrial_testing_config.json
index 67e864ae..c665245 100644
--- a/testing/variations/fieldtrial_testing_config.json
+++ b/testing/variations/fieldtrial_testing_config.json
@@ -946,7 +946,7 @@
             ],
             "experiments": [
                 {
-                    "name": "Expected",
+                    "name": "SuppressionExperiment",
                     "params": {
                         "contextual-search-ranker-model-url": "https://www.gstatic.com/chrome/intelligence/assist/ranker/models/contextual_search/test_ranker_model_20171109_short_words_v2.pb.bin",
                         "enable_bar_overlap_suppression": "true"
@@ -1952,6 +1952,24 @@
             ]
         }
     ],
+    "MemoryAblation": [
+        {
+            "platforms": [
+                "android"
+            ],
+            "experiments": [
+                {
+                    "name": "Enabled",
+                    "params": {
+                        "Size": "1048570"
+                    },
+                    "enable_features": [
+                        "MemoryAblation"
+                    ]
+                }
+            ]
+        }
+    ],
     "MojoCdm": [
         {
             "platforms": [
diff --git a/third_party/WebKit/LayoutTests/FlagExpectations/enable-blink-features=LayoutNG b/third_party/WebKit/LayoutTests/FlagExpectations/enable-blink-features=LayoutNG
index 76ee0a4..b546f5f 100644
--- a/third_party/WebKit/LayoutTests/FlagExpectations/enable-blink-features=LayoutNG
+++ b/third_party/WebKit/LayoutTests/FlagExpectations/enable-blink-features=LayoutNG
@@ -2205,7 +2205,6 @@
 crbug.com/591099 fast/block/positioning/positioned-child-inside-relative-positioned-anonymous-block.html [ Crash ]
 crbug.com/591099 fast/block/positioning/rel-positioned-inline-changes-width.html [ Crash ]
 crbug.com/591099 fast/block/positioning/relative-overflow-replaced.html [ Failure ]
-crbug.com/591099 fast/block/positioning/rtl-static-positioning.html [ Failure ]
 crbug.com/591099 fast/block/positioning/table-cell-static-position.html [ Failure ]
 crbug.com/591099 fast/block/positioning/vertical-rl/002.html [ Failure ]
 crbug.com/591099 fast/body-propagation/background-image/003-declarative.xhtml [ Failure Pass ]
@@ -2467,8 +2466,6 @@
 crbug.com/714962 fast/css/border-current-color.html [ Failure ]
 crbug.com/591099 fast/css/border-radius-outline-offset.html [ Failure ]
 crbug.com/591099 fast/css/case-transform.html [ Failure ]
-crbug.com/591099 fast/css/center-align-absolute-position-inline-block.html [ Failure ]
-crbug.com/591099 fast/css/center-align-absolute-position.html [ Failure ]
 crbug.com/591099 fast/css/checked-pseudo-selector.html [ Failure ]
 crbug.com/591099 fast/css/clip-zooming.html [ Failure ]
 crbug.com/591099 fast/css/color-correction-on-backgrounds.html [ Failure ]
@@ -3173,7 +3170,6 @@
 crbug.com/591099 fast/inline/inline-offsetLeft-relpos.html [ Failure ]
 crbug.com/591099 fast/inline/inline-with-empty-inline-children.html [ Failure ]
 crbug.com/591099 fast/inline/justify-emphasis-inline-box.html [ Failure ]
-crbug.com/591099 fast/inline/left-right-center-inline-alignment-in-ltr-and-rtl-blocks.html [ Failure ]
 crbug.com/591099 fast/inline/nested-text-descendants.html [ Failure ]
 crbug.com/591099 fast/inline/outline-continuations.html [ Failure ]
 crbug.com/714962 fast/inline/outline-offset.html [ Failure ]
diff --git a/third_party/WebKit/LayoutTests/TestExpectations b/third_party/WebKit/LayoutTests/TestExpectations
index 48cbbb8..18d3a3b 100644
--- a/third_party/WebKit/LayoutTests/TestExpectations
+++ b/third_party/WebKit/LayoutTests/TestExpectations
@@ -1189,7 +1189,7 @@
 crbug.com/662010 [ Win7 ] http/tests/csspaint/invalidation-background-image.html [ Skip ]
 
 crbug.com/807152 vr/VRDisplay_rAF_fires_with_window_rAF.html [ Pass Failure ]
-crbug.com/813697 [ Win7 ] vr/getFrameData_oneframeupdate.html [ Pass Failure ]
+crbug.com/813697 vr/getFrameData_oneframeupdate.html [ Pass Failure ]
 
 # These tests are skipped as there is no touch support on Mac.
 crbug.com/613672 [ Mac ] fast/events/touch/multi-touch-user-gesture.html [ Skip ]
@@ -3218,10 +3218,6 @@
 crbug.com/804682 [ Mac ] external/wpt/service-workers/service-worker/navigation-preload/resource-timing.https.html [ Pass Failure ]
 crbug.com/799137 [ Mac ] virtual/modern-media-controls/media/controls/modern/doubletap-to-jump-backwards-at-start.html [ Pass Timeout ]
 
-# Sheriff failures 2018-01-08
-# This test fails consistently on Nexus4 - see bug for info.
-crbug.com/799814 [ Android ] fast/beacon/beacon-basic.html [ Pass Failure ]
-
 # This test is flaking (failing, crashing) on mac_chromium_rel_ng.
 crbug.com/800840 [ Mac ] virtual/wheelscrolllatching/fast/compositor-wheel-scroll-latching/touchpad-scroll-impl-to-main.html [ Skip ]
 
@@ -3251,13 +3247,13 @@
 crbug.com/813462 external/wpt/offscreen-canvas/the-offscreen-canvas/offscreencanvas.convert.to.blob.html [ Pass Timeout ]
 
 # Sheriff failures 2018-02-20
-crbug.com/770691 [ Mac10.11 ] media/controls/repaint-on-resize.html [ Failure Pass ]
+crbug.com/789921 media/controls/repaint-on-resize.html [ Failure Pass ]
 
 # Sheriff failures 2018-02-21
 crbug.com/814585 [ Linux ] fast/css3-text/css3-text-decoration/text-underline-position/text-underline-position-cjk.html [ Pass Failure ]
 
 # Sheriff failures 2018-02-22
-crbug.com/770691 [ Mac10.11 ] virtual/new-remote-playback-pipeline/media/controls/repaint-on-resize.html [ Failure Pass ]
+crbug.com/789921 virtual/new-remote-playback-pipeline/media/controls/repaint-on-resize.html [ Failure Pass ]
 
 # MSAN
 crbug.com/813547 [ Linux ] webaudio/BiquadFilter/tail-time-lowpass.html [ Pass Failure ]
@@ -3351,3 +3347,7 @@
 crbug.com/812941 [ Win7 ] external/wpt/css/css-multicol/multicol-table-cell-vertical-align-001.xht [ Failure Pass ]
 
 crbug.com/813704 [ Win7 Mac ] http/tests/images/png-partial-load-as-document.html [ Failure Pass ]
+
+crbug.com/814889 [ Debug ] idle-callback/test-runner-run-idle-tasks.html [ Timeout Pass ]
+
+crbug.com/814907 http/tests/credentialmanager/credentialscontainer-create-from-nested-frame.html [ Failure Pass ]
diff --git a/third_party/WebKit/LayoutTests/W3CImportExpectations b/third_party/WebKit/LayoutTests/W3CImportExpectations
index b4f4b53..4756e01 100644
--- a/third_party/WebKit/LayoutTests/W3CImportExpectations
+++ b/third_party/WebKit/LayoutTests/W3CImportExpectations
@@ -81,7 +81,6 @@
 external/wpt/css/CSS2/zorder [ Skip ]
 external/wpt/css/WOFF2 [ Skip ]
 external/wpt/css/compositing [ Skip ]
-external/wpt/css/css-animations [ Skip ]
 external/wpt/css/css-counter-styles [ Skip ]
 external/wpt/css/css-display/run-in [ Skip ]
 external/wpt/css/css-exclusions [ Skip ]
diff --git a/third_party/WebKit/LayoutTests/accessibility/aom-computed-float-properties.html b/third_party/WebKit/LayoutTests/accessibility/aom-computed-float-properties.html
new file mode 100644
index 0000000..e393be32
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/accessibility/aom-computed-float-properties.html
@@ -0,0 +1,53 @@
+<!DOCTYPE HTML>
+<script src="../resources/gc.js"></script>
+<script src="../resources/testharness.js"></script>
+<script src="../resources/testharnessreport.js"></script>
+
+<!--
+
+Accessibility Object Model
+Explainer: https://github.com/WICG/aom/blob/master/explainer.md
+Spec: https://wicg.github.io/aom/spec/
+
+-->
+
+<input id="native-slider" type="range" min="1" max="100" value="50">
+<div id="aria-slider" role="slider" aria-valuemin="1" aria-valuemax="100" aria-valuenow="50">
+
+<script>
+
+test(function(t) {
+    assert_true(internals.runtimeFlags.accessibilityObjectModelEnabled);
+}, "Make sure that Accessibility Object Model is enabled");
+
+promise_test(async function(t) {
+  var nativeElement = document.getElementById("native-slider");
+  var ariaElement = document.getElementById("aria-slider");
+  var nativeCaxNode = await window.getComputedAccessibleNode(nativeElement);
+  var ariaCaxNode = await window.getComputedAccessibleNode(ariaElement);
+
+  assert_equals(nativeCaxNode.valueMin, 1);
+  assert_equals(ariaCaxNode.valueMin, 1);
+}, "ComputedAccessibleNode.valueMin");
+
+promise_test(async function(t) {
+  var nativeElement = document.getElementById("native-slider");
+  var ariaElement = document.getElementById("aria-slider");
+  var nativeCaxNode = await window.getComputedAccessibleNode(nativeElement);
+  var ariaCaxNode = await window.getComputedAccessibleNode(ariaElement);
+
+  assert_equals(nativeCaxNode.valueMax, 100);
+  assert_equals(ariaCaxNode.valueMax, 100);
+}, "ComputedAccessibleNode.valueMax");
+
+promise_test(async function(t) {
+  var nativeElement = document.getElementById("native-slider");
+  var ariaElement = document.getElementById("aria-slider");
+  var nativeCaxNode = await window.getComputedAccessibleNode(nativeElement);
+  var ariaCaxNode = await window.getComputedAccessibleNode(ariaElement);
+
+  assert_equals(nativeCaxNode.valueNow, 50);
+  assert_equals(ariaCaxNode.valueNow, 50);
+}, "ComputedAccessibleNode.valueNow");
+
+</script>
diff --git a/third_party/WebKit/LayoutTests/external/wpt/css/css-typed-om/stylevalue-normalization/normalize-resource.tentative.html b/third_party/WebKit/LayoutTests/external/wpt/css/css-typed-om/stylevalue-normalization/normalize-resource.tentative.html
index d5082aa..a9722774 100644
--- a/third_party/WebKit/LayoutTests/external/wpt/css/css-typed-om/stylevalue-normalization/normalize-resource.tentative.html
+++ b/third_party/WebKit/LayoutTests/external/wpt/css/css-typed-om/stylevalue-normalization/normalize-resource.tentative.html
@@ -9,7 +9,7 @@
 <script>
 'use strict';
 
-const gTestUrl = '../resources/1x1-green.png';
+const gTestUrl = '/media/1x1-green.png';
 const gBadTestUrl = document.location.href;
 
 async_test(t => {
diff --git a/third_party/WebKit/LayoutTests/external/wpt/css/css-typed-om/stylevalue-subclasses/cssUrlImageValue.html b/third_party/WebKit/LayoutTests/external/wpt/css/css-typed-om/stylevalue-subclasses/cssUrlImageValue.html
index 5025859..abc944f 100644
--- a/third_party/WebKit/LayoutTests/external/wpt/css/css-typed-om/stylevalue-subclasses/cssUrlImageValue.html
+++ b/third_party/WebKit/LayoutTests/external/wpt/css/css-typed-om/stylevalue-subclasses/cssUrlImageValue.html
@@ -11,7 +11,7 @@
 <script>
 'use strict';
 
-const gTestUrl = '../resources/1x1-green.png';
+const gTestUrl = '/media/1x1-green.png';
 const gBase64TestUrl = 'data:image/gif;base64,R0lGODlhAQABAAAAACwAAAAAAQABAAA=';
 const gBadTestUrl = document.location.href;
 
diff --git a/third_party/WebKit/LayoutTests/external/wpt/css/css-typed-om/resources/1x1-green.png b/third_party/WebKit/LayoutTests/external/wpt/media/1x1-green.png
similarity index 100%
rename from third_party/WebKit/LayoutTests/external/wpt/css/css-typed-om/resources/1x1-green.png
rename to third_party/WebKit/LayoutTests/external/wpt/media/1x1-green.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/external/wpt/svg/foreignobject/foreign-object-circular-filter-reference-crash.html b/third_party/WebKit/LayoutTests/external/wpt/svg/foreignobject/foreign-object-circular-filter-reference-crash.html
new file mode 100644
index 0000000..1629994
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/svg/foreignobject/foreign-object-circular-filter-reference-crash.html
@@ -0,0 +1,13 @@
+<!DOCTYPE html>
+<title>Should not crash on circular filter reference containing a foreignObject.</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script>test(()=>{})</script>
+<svg id="svg" filter="url(#filter)">
+  <foreignObject overflow="hidden"></foreignObject>
+</svg>
+<svg>
+  <filter id="filter">
+    <feImage xlink:href="#svg"></feImage>
+  </filter>
+</svg>
diff --git a/third_party/WebKit/LayoutTests/external/wpt/svg/svg-in-svg/svg-in-svg-circular-filter-reference-crash.html b/third_party/WebKit/LayoutTests/external/wpt/svg/svg-in-svg/svg-in-svg-circular-filter-reference-crash.html
new file mode 100644
index 0000000..51303171
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/svg/svg-in-svg/svg-in-svg-circular-filter-reference-crash.html
@@ -0,0 +1,13 @@
+<!DOCTYPE html>
+<title>Should not crash on circular filter reference containing an svg under svg.</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script>test(()=>{})</script>
+<svg id="svg" filter="url(#filter)">
+  <filter id="filter">
+    <feImage xlink:href="#svg"></feImage>
+  </filter>
+  <svg overflow="hidden" width="20" height="20">
+    <rect fill="black" width="20" height="20"></rect>
+  </svg>
+</svg>
diff --git a/third_party/WebKit/LayoutTests/fast/canvas-api/canvas-hit-regions-css-transform-test.html b/third_party/WebKit/LayoutTests/fast/canvas-api/canvas-hit-regions-css-transform-test.html
index 101802f..db6d78e3 100644
--- a/third_party/WebKit/LayoutTests/fast/canvas-api/canvas-hit-regions-css-transform-test.html
+++ b/third_party/WebKit/LayoutTests/fast/canvas-api/canvas-hit-regions-css-transform-test.html
@@ -24,7 +24,7 @@
   var paddingLeft = parseInt(canvas.style.paddingLeft) || 0;
   var borderLeft = parseInt(canvas.style.borderLeft) || 0;
   var cssWidth = parseInt(canvas.style.width) || canvas.width;
-  var scale = cssWidth / canvas.width;
+  var scale = canvas.width == 0 ? 1 : cssWidth / canvas.width;
   var tx = x;
   if (degree) {
     var cos = Math.cos(degree * Math.PI / 180);
@@ -41,7 +41,7 @@
   var paddingTop = parseInt(canvas.style.paddingTop) || 0;
   var borderTop = parseInt(canvas.style.borderTop) || 0;
   var cssHeight = parseInt(canvas.style.height) || canvas.height;
-  var scale = cssHeight / canvas.height;
+  var scale = canvas.height == 0 ? 1 : cssHeight / canvas.height;
   var ty = y;
   if (degree) {
     var cos = Math.cos(degree * Math.PI / 180);
@@ -131,6 +131,15 @@
   canvas.style.transform = 'rotate(72deg)';
   yield hit_region_with_css_test(test_set_with_rotate);
 
+  canvas.width = '0';
+  canvas.height = '0';
+  canvas.style.width = "0px";
+  canvas.style.height = "0px";
+  var test_divide_zero = [
+      { id : null, x : 20, y : 10, name: 'null' }
+  ];
+  yield hit_region_with_css_test(test_divide_zero);
+
   done();
 });
 
diff --git a/third_party/WebKit/LayoutTests/fast/text/font-format-support-cbdt-sbix-cff2-vertical.html b/third_party/WebKit/LayoutTests/fast/text/font-format-support-cbdt-sbix-cff2-vertical.html
new file mode 100644
index 0000000..79fc3b9
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/fast/text/font-format-support-cbdt-sbix-cff2-vertical.html
@@ -0,0 +1,62 @@
+<html>
+<meta charset="UTF-8">
+ <style type="text/css">
+    @font-face {
+        font-family: 'adobevfproto';
+        src: url("../../third_party/AdobeVF/AdobeVFPrototype.otf");
+    }
+    @font-face {
+        font-family: 'chromacheck-sbix';
+        src: url("../../third_party/ChromaCheck/chromacheck-sbix.woff");
+    }
+    @font-face {
+        font-family: 'chromacheck-cbdt';
+        src: url("../../third_party/ChromaCheck/chromacheck-cbdt.woff");
+    }
+
+    .cff2_test {
+        font-family: adobevfproto, sans-serif;
+    }
+    .sbix_test {
+        font-family: chromacheck-sbix, sans-serif;
+    }
+    .cbdt_test {
+        font-family: chromacheck-cbdt, sans-serif;
+    }
+
+    .horizontalgrid {
+        display: flex;
+        height: 50%;
+    }
+
+    .gridcell {
+        flex: 1;
+        padding: 10px;
+        overflow: visible;
+        writing-mode: vertical-rl;
+    }
+
+    .upright {
+        text-orientation: upright;
+    }
+
+    p {
+        margin-bottom: 1em;
+    }
+ </style>
+<body>
+    <div class="horizontalgrid">
+        <div class="gridcell"><p>Should show as a serif font, not as a sans-serif fallback:</p><span class="cff2_test">AbcDef</span></div>
+        <div class="gridcell"><p>Should show red squares:</p><span class="sbix_test">&#xE901; &#xE901; &#xE901; &#xE901;
+        &#xE901;</span></div>
+        <div class="gridcell"><p>Should show dark red squares:</p><span class="cbdt_test">&#xE903; &#xE903; &#xE903;
+        &#xE903; &#xE903;</span></div>
+    </div>
+    <div class="horizontalgrid">
+        <div class="gridcell"><p>Should show as a serif font, not as a sans-serif fallback:</p><span class="cff2_test upright">AbcDef</span></div>
+        <div class="gridcell"><p>Should show red squares:</p><span class="sbix_test upright">&#xE901; &#xE901; &#xE901;
+        &#xE901; &#xE901;</span></div>
+        <div class="gridcell"><p>Should show dark red squares:</p><span class="cbdt_test upright">&#xE903; &#xE903; &#xE903; &#xE903; &#xE903;</span></div>
+    </div>
+</body>
+</html>
diff --git a/third_party/WebKit/LayoutTests/fast/text/font-format-support-cbdt-sbix-cff2.html b/third_party/WebKit/LayoutTests/fast/text/font-format-support-cbdt-sbix-cff2.html
new file mode 100644
index 0000000..bbffa92
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/fast/text/font-format-support-cbdt-sbix-cff2.html
@@ -0,0 +1,68 @@
+<html>
+<meta charset="UTF-8">
+ <style type="text/css">
+    @font-face {
+        font-family: 'adobevfproto';
+        src: url("../../third_party/AdobeVF/AdobeVFPrototype.otf");
+    }
+    @font-face {
+        font-family: 'chromacheck-sbix';
+        src: url("../../third_party/ChromaCheck/chromacheck-sbix.woff");
+    }
+    @font-face {
+        font-family: 'chromacheck-cbdt';
+        src: url("../../third_party/ChromaCheck/chromacheck-cbdt.woff");
+    }
+
+    .cff2_test {
+        font-family: adobevfproto, sans-serif;
+    }
+    .sbix_test {
+        font-family: chromacheck-sbix, sans-serif;
+    }
+    .cbdt_test {
+        font-family: chromacheck-cbdt, sans-serif;
+    }
+
+    .horizontalgrid {
+        display: flex;
+    }
+
+    .gridcell {
+        flex: 1;
+        padding: 10px;
+        overflow: visible;
+    }
+
+    p {
+        margin-bottom: 1em;
+    }
+ </style>
+<body>
+    <div class="horizontalgrid">
+        <div class="gridcell"><p>Should show as a serif font, not as a sans-serif fallback:</p><span class="cff2_test"></span></div>
+        <div class="gridcell"><p>Should show red squares:</p><span class="sbix_test"></span></div>
+        <div class="gridcell"><p>Should show dark red squares:</p><span class="cbdt_test"></span></div>
+    </div>
+    <script>
+      function appendRotatedScale(theNode, testText) {
+          var fontSize = 3;
+          var angle = 15;
+          for (var i = 0; i < 7; ++i) {
+              fontSize = fontSize * 2;
+              theDiv = document.createElement("div");
+              theDiv.style.position = "absolute";
+              theDiv.style.fontSize = fontSize + "px";
+              theDiv.style.transformOrigin = "left bottom";
+              theDiv.style.transform = "rotateZ(" + angle * i + "deg) translate(" + 10 * i + "px)";
+              theDiv.innerText = testText;
+              theNode.appendChild(theDiv);
+          }
+      }
+
+      appendRotatedScale(document.querySelector(".cff2_test"), "y");
+      appendRotatedScale(document.querySelector(".sbix_test"), "\uE901");
+      appendRotatedScale(document.querySelector(".cbdt_test"), "\uE903");
+     </script>
+</body>
+</html>
diff --git a/third_party/WebKit/LayoutTests/http/tests/credentialmanager/credentialscontainer-create-basics.html b/third_party/WebKit/LayoutTests/http/tests/credentialmanager/credentialscontainer-create-basics.html
index 46fef25..7fa2635 100644
--- a/third_party/WebKit/LayoutTests/http/tests/credentialmanager/credentialscontainer-create-basics.html
+++ b/third_party/WebKit/LayoutTests/http/tests/credentialmanager/credentialscontainer-create-basics.html
@@ -174,13 +174,6 @@
       navigator.credentials.create({ publicKey : MAKE_CREDENTIAL_OPTIONS}));
 }, "Verify that not supported error returned by mock is properly handled.");
 
-promise_test(t => {
-  mockAuthenticator.setAuthenticatorStatus(
-      webauth.mojom.AuthenticatorStatus.TIMED_OUT);
-  return promise_rejects(t, "NotAllowedError",
-      navigator.credentials.create({ publicKey : MAKE_CREDENTIAL_OPTIONS}));
-}, "Verify that timed out error returned by mock is properly handled.");
-
 promise_test(_ => {
   mockAuthenticator.reset();
   mockAuthenticator.setRawId(RAW_ID);
diff --git a/third_party/WebKit/LayoutTests/http/tests/credentialmanager/credentialscontainer-get-basics.html b/third_party/WebKit/LayoutTests/http/tests/credentialmanager/credentialscontainer-get-basics.html
index a14b0ff6..b64617e2 100644
--- a/third_party/WebKit/LayoutTests/http/tests/credentialmanager/credentialscontainer-get-basics.html
+++ b/third_party/WebKit/LayoutTests/http/tests/credentialmanager/credentialscontainer-get-basics.html
@@ -153,13 +153,6 @@
       navigator.credentials.get({ publicKey : GET_CREDENTIAL_OPTIONS}));
 }, "Verify that not supported error returned by mock is properly handled.");
 
-promise_test(t => {
-  mockAuthenticator.setAuthenticatorStatus(
-      webauth.mojom.AuthenticatorStatus.TIMED_OUT);
-  return promise_rejects(t, "NotAllowedError",
-      navigator.credentials.get({ publicKey : GET_CREDENTIAL_OPTIONS}));
-}, "Verify that timed out error returned by mock is properly handled.");
-
 promise_test(function(t) {
     var customGetCredentialOptions = {
         // No challenge.
diff --git a/third_party/WebKit/LayoutTests/inspector-protocol/layout-fonts/cross-platform-cbdt-sbix-cff2-expected.txt b/third_party/WebKit/LayoutTests/inspector-protocol/layout-fonts/cross-platform-cbdt-sbix-cff2-expected.txt
new file mode 100644
index 0000000..acb88eab
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/inspector-protocol/layout-fonts/cross-platform-cbdt-sbix-cff2-expected.txt
@@ -0,0 +1,13 @@
+abcdefghijklmnopqrstuvwxyz
+#cff2_support__should_be_using_adobe_variable_font_protoptype_only:
+"Adobe Variable Font Prototype" : 26
+
+
+#sbix_support__should_be_using_chromacheck_only:
+"PixelAmbacht ChromaCheck" : 7
+
+
+#cbdt_support__should_be_using_notocoloremoji_only:
+"ChromaCheck CBDT" : 7
+
+
diff --git a/third_party/WebKit/LayoutTests/inspector-protocol/layout-fonts/cross-platform-cbdt-sbix-cff2.js b/third_party/WebKit/LayoutTests/inspector-protocol/layout-fonts/cross-platform-cbdt-sbix-cff2.js
new file mode 100644
index 0000000..c1c174d4
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/inspector-protocol/layout-fonts/cross-platform-cbdt-sbix-cff2.js
@@ -0,0 +1,49 @@
+(async function(testRunner) {
+  var page = await testRunner.createPage();
+  await page.loadHTML(`
+    <html>
+    <meta charset="UTF-8">
+     <style type="text/css">
+        @font-face {
+            font-family: 'adobevfproto';
+            src: url("../../third_party/AdobeVF/AdobeVFPrototype.otf");
+        }
+        @font-face {
+            font-family: 'chromacheck-sbix';
+            src: url("../../third_party/ChromaCheck/chromacheck-sbix.woff");
+        }
+        @font-face {
+            font-family: 'chromacheck-cbdt';
+            src: url("../../third_party/ChromaCheck/chromacheck-cbdt.woff");
+        }
+
+        body {
+            font-size: 40px;
+        }
+
+        .cff2_test {
+            font-family: adobevfproto, sans-serif;
+        }
+
+        .sbix_test {
+            font-family: chromacheck-sbix, sans-serif;
+        }
+        .cbdt_test {
+            font-family: chromacheck-cbdt, sans-serif;
+        }
+    </style>
+    <body>
+        <div class="test">
+            <div id="cff2_support__should_be_using_adobe_variable_font_protoptype_only" class="cff2_test">abcdefghijklmnopqrstuvwxyz</div>
+            <div id="sbix_support__should_be_using_chromacheck_only" class="sbix_test">&#xE901;&#xE901;&#xE901;&#xE901;&#xE901;&#xE901;&#xE901;</div>
+            <div id="cbdt_support__should_be_using_notocoloremoji_only" class="cbdt_test">&#xE903;&#xE903;&#xE903;&#xE903;&#xE903;&#xE903;&#xE903;</div>
+        </div>
+    </body>
+    </html>
+  `);
+  var session = await page.createSession();
+
+  var helper = await testRunner.loadScript('./resources/layout-font-test.js');
+  await helper(testRunner, session);
+  testRunner.completeTest();
+})
diff --git a/third_party/WebKit/LayoutTests/platform/android/fast/beacon/beacon-basic-expected.txt b/third_party/WebKit/LayoutTests/platform/android/fast/beacon/beacon-basic-expected.txt
index 60597a9..f88f639 100644
--- a/third_party/WebKit/LayoutTests/platform/android/fast/beacon/beacon-basic-expected.txt
+++ b/third_party/WebKit/LayoutTests/platform/android/fast/beacon/beacon-basic-expected.txt
@@ -8,7 +8,6 @@
 PASS navigator.sendBeacon() threw exception TypeError: Failed to execute 'sendBeacon' on 'Navigator': 1 argument required, but only 0 present..
 FAIL navigator.sendBeacon('http:') should throw an exception. Was true.
 PASS navigator.sendBeacon('javascript:alert(1);') threw exception TypeError: Failed to execute 'sendBeacon' on 'Navigator': Beacons are only supported over HTTP(S)..
-PASS navigator.sendBeacon('https:', new Uint8Array(new SharedArrayBuffer(10))) threw exception TypeError: Failed to execute 'sendBeacon' on 'Navigator': The provided ArrayBufferView value must not be shared..
 PASS successfullyParsed is true
 
 TEST COMPLETE
diff --git a/third_party/WebKit/LayoutTests/platform/linux/fast/text/font-format-support-cbdt-sbix-cff2-expected.png b/third_party/WebKit/LayoutTests/platform/linux/fast/text/font-format-support-cbdt-sbix-cff2-expected.png
new file mode 100644
index 0000000..813535d7
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/platform/linux/fast/text/font-format-support-cbdt-sbix-cff2-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/linux/fast/text/font-format-support-cbdt-sbix-cff2-vertical-expected.png b/third_party/WebKit/LayoutTests/platform/linux/fast/text/font-format-support-cbdt-sbix-cff2-vertical-expected.png
new file mode 100644
index 0000000..84ff103e
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/platform/linux/fast/text/font-format-support-cbdt-sbix-cff2-vertical-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/mac-mac10.10/fast/text/font-format-support-cbdt-sbix-cff2-expected.png b/third_party/WebKit/LayoutTests/platform/mac-mac10.10/fast/text/font-format-support-cbdt-sbix-cff2-expected.png
new file mode 100644
index 0000000..7aa52b2a
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/platform/mac-mac10.10/fast/text/font-format-support-cbdt-sbix-cff2-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/mac-mac10.10/fast/text/font-format-support-cbdt-sbix-cff2-vertical-expected.png b/third_party/WebKit/LayoutTests/platform/mac-mac10.10/fast/text/font-format-support-cbdt-sbix-cff2-vertical-expected.png
new file mode 100644
index 0000000..d385e97
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/platform/mac-mac10.10/fast/text/font-format-support-cbdt-sbix-cff2-vertical-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/mac-mac10.11/fast/text/font-format-support-cbdt-sbix-cff2-expected.png b/third_party/WebKit/LayoutTests/platform/mac-mac10.11/fast/text/font-format-support-cbdt-sbix-cff2-expected.png
new file mode 100644
index 0000000..7e85366
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/platform/mac-mac10.11/fast/text/font-format-support-cbdt-sbix-cff2-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/mac-mac10.11/fast/text/font-format-support-cbdt-sbix-cff2-vertical-expected.png b/third_party/WebKit/LayoutTests/platform/mac-mac10.11/fast/text/font-format-support-cbdt-sbix-cff2-vertical-expected.png
new file mode 100644
index 0000000..9a24cff
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/platform/mac-mac10.11/fast/text/font-format-support-cbdt-sbix-cff2-vertical-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/mac-mac10.12/fast/text/font-format-support-cbdt-sbix-cff2-expected.png b/third_party/WebKit/LayoutTests/platform/mac-mac10.12/fast/text/font-format-support-cbdt-sbix-cff2-expected.png
new file mode 100644
index 0000000..5fcdf78d7
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/platform/mac-mac10.12/fast/text/font-format-support-cbdt-sbix-cff2-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/mac-mac10.12/fast/text/font-format-support-cbdt-sbix-cff2-vertical-expected.png b/third_party/WebKit/LayoutTests/platform/mac-mac10.12/fast/text/font-format-support-cbdt-sbix-cff2-vertical-expected.png
new file mode 100644
index 0000000..44532598f
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/platform/mac-mac10.12/fast/text/font-format-support-cbdt-sbix-cff2-vertical-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/mac-retina/fast/text/font-format-support-cbdt-sbix-cff2-expected.png b/third_party/WebKit/LayoutTests/platform/mac-retina/fast/text/font-format-support-cbdt-sbix-cff2-expected.png
new file mode 100644
index 0000000..5fcdf78d7
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/platform/mac-retina/fast/text/font-format-support-cbdt-sbix-cff2-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/mac-retina/fast/text/font-format-support-cbdt-sbix-cff2-vertical-expected.png b/third_party/WebKit/LayoutTests/platform/mac-retina/fast/text/font-format-support-cbdt-sbix-cff2-vertical-expected.png
new file mode 100644
index 0000000..44532598f
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/platform/mac-retina/fast/text/font-format-support-cbdt-sbix-cff2-vertical-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/mac/fast/text/font-format-support-cbdt-sbix-cff2-expected.png b/third_party/WebKit/LayoutTests/platform/mac/fast/text/font-format-support-cbdt-sbix-cff2-expected.png
new file mode 100644
index 0000000..96635f4
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/platform/mac/fast/text/font-format-support-cbdt-sbix-cff2-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/mac/fast/text/font-format-support-cbdt-sbix-cff2-vertical-expected.png b/third_party/WebKit/LayoutTests/platform/mac/fast/text/font-format-support-cbdt-sbix-cff2-vertical-expected.png
new file mode 100644
index 0000000..5c7e7ad5
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/platform/mac/fast/text/font-format-support-cbdt-sbix-cff2-vertical-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/win/fast/text/font-format-support-cbdt-sbix-cff2-expected.png b/third_party/WebKit/LayoutTests/platform/win/fast/text/font-format-support-cbdt-sbix-cff2-expected.png
new file mode 100644
index 0000000..d0271788
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/platform/win/fast/text/font-format-support-cbdt-sbix-cff2-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/win/fast/text/font-format-support-cbdt-sbix-cff2-vertical-expected.png b/third_party/WebKit/LayoutTests/platform/win/fast/text/font-format-support-cbdt-sbix-cff2-vertical-expected.png
new file mode 100644
index 0000000..2aa04f2
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/platform/win/fast/text/font-format-support-cbdt-sbix-cff2-vertical-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/win7/fast/text/font-format-support-cbdt-sbix-cff2-expected.png b/third_party/WebKit/LayoutTests/platform/win7/fast/text/font-format-support-cbdt-sbix-cff2-expected.png
new file mode 100644
index 0000000..78454c7
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/platform/win7/fast/text/font-format-support-cbdt-sbix-cff2-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/win7/fast/text/font-format-support-cbdt-sbix-cff2-vertical-expected.png b/third_party/WebKit/LayoutTests/platform/win7/fast/text/font-format-support-cbdt-sbix-cff2-vertical-expected.png
new file mode 100644
index 0000000..1e5692d
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/platform/win7/fast/text/font-format-support-cbdt-sbix-cff2-vertical-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/third_party/AdobeVF/AdobeVFPrototype.otf b/third_party/WebKit/LayoutTests/third_party/AdobeVF/AdobeVFPrototype.otf
new file mode 100644
index 0000000..e9b9220a
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/third_party/AdobeVF/AdobeVFPrototype.otf
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/third_party/AdobeVF/LICENSE.md b/third_party/WebKit/LayoutTests/third_party/AdobeVF/LICENSE.md
new file mode 100644
index 0000000..d952d62
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/third_party/AdobeVF/LICENSE.md
@@ -0,0 +1,92 @@
+This Font Software is licensed under the SIL Open Font License,
+Version 1.1.
+
+This license is copied below, and is also available with a FAQ at:
+http://scripts.sil.org/OFL
+
+-----------------------------------------------------------
+SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007
+-----------------------------------------------------------
+
+PREAMBLE
+The goals of the Open Font License (OFL) are to stimulate worldwide
+development of collaborative font projects, to support the font
+creation efforts of academic and linguistic communities, and to
+provide a free and open framework in which fonts may be shared and
+improved in partnership with others.
+
+The OFL allows the licensed fonts to be used, studied, modified and
+redistributed freely as long as they are not sold by themselves. The
+fonts, including any derivative works, can be bundled, embedded,
+redistributed and/or sold with any software provided that any reserved
+names are not used by derivative works. The fonts and derivatives,
+however, cannot be released under any other type of license. The
+requirement for fonts to remain under this license does not apply to
+any document created using the fonts or their derivatives.
+
+DEFINITIONS
+"Font Software" refers to the set of files released by the Copyright
+Holder(s) under this license and clearly marked as such. This may
+include source files, build scripts and documentation.
+
+"Reserved Font Name" refers to any names specified as such after the
+copyright statement(s).
+
+"Original Version" refers to the collection of Font Software
+components as distributed by the Copyright Holder(s).
+
+"Modified Version" refers to any derivative made by adding to,
+deleting, or substituting -- in part or in whole -- any of the
+components of the Original Version, by changing formats or by porting
+the Font Software to a new environment.
+
+"Author" refers to any designer, engineer, programmer, technical
+writer or other person who contributed to the Font Software.
+
+PERMISSION & CONDITIONS
+Permission is hereby granted, free of charge, to any person obtaining
+a copy of the Font Software, to use, study, copy, merge, embed,
+modify, redistribute, and sell modified and unmodified copies of the
+Font Software, subject to the following conditions:
+
+1) Neither the Font Software nor any of its individual components, in
+Original or Modified Versions, may be sold by itself.
+
+2) Original or Modified Versions of the Font Software may be bundled,
+redistributed and/or sold with any software, provided that each copy
+contains the above copyright notice and this license. These can be
+included either as stand-alone text files, human-readable headers or
+in the appropriate machine-readable metadata fields within text or
+binary files as long as those fields can be easily viewed by the user.
+
+3) No Modified Version of the Font Software may use the Reserved Font
+Name(s) unless explicit written permission is granted by the
+corresponding Copyright Holder. This restriction only applies to the
+primary font name as presented to the users.
+
+4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font
+Software shall not be used to promote, endorse or advertise any
+Modified Version, except to acknowledge the contribution(s) of the
+Copyright Holder(s) and the Author(s) or with their explicit written
+permission.
+
+5) The Font Software, modified or unmodified, in part or in whole,
+must be distributed entirely under this license, and must not be
+distributed under any other license. The requirement for fonts to
+remain under this license does not apply to any document created using
+the Font Software.
+
+TERMINATION
+This license becomes null and void if any of the above conditions are
+not met.
+
+DISCLAIMER
+THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT
+OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE
+COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL
+DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM
+OTHER DEALINGS IN THE FONT SOFTWARE.
diff --git a/third_party/WebKit/LayoutTests/third_party/AdobeVF/README.chromium b/third_party/WebKit/LayoutTests/third_party/AdobeVF/README.chromium
new file mode 100644
index 0000000..a806b4b2
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/third_party/AdobeVF/README.chromium
@@ -0,0 +1,10 @@
+Adobe Variable Font Prototype
+
+1. Source Origin
+https://github.com/adobe-fonts/adobe-variable-font-prototype
+
+2. License
+For license information, see the LICENSE.md file.
+
+3. Reason
+Used for LayoutTest'ing cross platform CFF2 font outline support.
diff --git a/third_party/WebKit/LayoutTests/third_party/ChromaCheck/LICENSE.md b/third_party/WebKit/LayoutTests/third_party/ChromaCheck/LICENSE.md
new file mode 100644
index 0000000..13aea605
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/third_party/ChromaCheck/LICENSE.md
@@ -0,0 +1,21 @@
+The MIT License (MIT)
+
+Copyright (c) 2016 Roel Nieskens, https://pixelambacht.nl
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
diff --git a/third_party/WebKit/LayoutTests/third_party/ChromaCheck/README.chromium b/third_party/WebKit/LayoutTests/third_party/ChromaCheck/README.chromium
new file mode 100644
index 0000000..587b104
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/third_party/ChromaCheck/README.chromium
@@ -0,0 +1,25 @@
+ChromaCheck Sbix
+
+1. Source Origin
+https://github.com/RoelN/ChromaCheck/blob/master/fonts/chromacheck-sbix.woff
+Version: 8dadeaafb179b5571e6acdad25f4660a1cdcd395
+
+2. License
+For license information, see the LICENSE.md file.
+
+3. Reason
+Used for LayoutTest'ing cross platform Sbix bitmap font support.
+
+Sbix compatibility note:
+On 1/11/2018 Roel committed:
+https://github.com/RoelN/ChromaCheck/commit/8e4c22477830a42eac7012e26e5b346c81d656a2#diff-ca9080785e92b181eb1570ce0eb6da61
+which changed the font files and added a glyf outline to the uniE901 glyph in
+the sbix test file.
+
+SBIX fails to work correctly if there are no place holder outlines in contours
+for a glyph in the glyf table. With the newer version, the tests pass.
+
+Mac OS < 10.13 seems to have trouble determining the bounding box for SBIX fonts
+which have non multiple of hundred units per em values. I.e. 1024 instead of
+800, as the system emoji font. This is fixed in ChromaCheck fonts after revision
+8dadeaafb179b5571e6acdad25f4660a1cdcd395.
diff --git a/third_party/WebKit/LayoutTests/third_party/ChromaCheck/chromacheck-cbdt.woff b/third_party/WebKit/LayoutTests/third_party/ChromaCheck/chromacheck-cbdt.woff
new file mode 100644
index 0000000..49804ca3
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/third_party/ChromaCheck/chromacheck-cbdt.woff
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/third_party/ChromaCheck/chromacheck-sbix.woff b/third_party/WebKit/LayoutTests/third_party/ChromaCheck/chromacheck-sbix.woff
new file mode 100644
index 0000000..518d4b7
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/third_party/ChromaCheck/chromacheck-sbix.woff
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/webexposed/global-interface-listing-expected.txt b/third_party/WebKit/LayoutTests/webexposed/global-interface-listing-expected.txt
index e7099bb..4eef16f8 100644
--- a/third_party/WebKit/LayoutTests/webexposed/global-interface-listing-expected.txt
+++ b/third_party/WebKit/LayoutTests/webexposed/global-interface-listing-expected.txt
@@ -1101,6 +1101,9 @@
     getter rowIndex
     getter rowSpan
     getter setSize
+    getter valueMax
+    getter valueMin
+    getter valueNow
     getter valueText
     method constructor
     method ensureUpToDate
diff --git a/third_party/WebKit/Source/core/dom/ComputedAccessibleNode.cpp b/third_party/WebKit/Source/core/dom/ComputedAccessibleNode.cpp
index 972142d..ea60fa4 100644
--- a/third_party/WebKit/Source/core/dom/ComputedAccessibleNode.cpp
+++ b/third_party/WebKit/Source/core/dom/ComputedAccessibleNode.cpp
@@ -212,6 +212,18 @@
   return GetIntAttribute(WebAOMIntAttribute::AOM_ATTR_SET_SIZE, is_null);
 }
 
+float ComputedAccessibleNode::valueMax(bool& is_null) const {
+  return GetFloatAttribute(WebAOMFloatAttribute::AOM_ATTR_VALUE_MAX, is_null);
+}
+
+float ComputedAccessibleNode::valueMin(bool& is_null) const {
+  return GetFloatAttribute(WebAOMFloatAttribute::AOM_ATTR_VALUE_MIN, is_null);
+}
+
+float ComputedAccessibleNode::valueNow(bool& is_null) const {
+  return GetFloatAttribute(WebAOMFloatAttribute::AOM_ATTR_VALUE_NOW, is_null);
+}
+
 ComputedAccessibleNode* ComputedAccessibleNode::parent() const {
   int32_t parent_ax_id;
   if (!tree_->GetParentIdForAXNode(ax_id_, &parent_ax_id)) {
@@ -286,6 +298,16 @@
   return out;
 }
 
+float ComputedAccessibleNode::GetFloatAttribute(WebAOMFloatAttribute attr,
+                                                bool& is_null) const {
+  float out;
+  is_null = true;
+  if (tree_->GetFloatAttributeForAXNode(ax_id_, attr, &out)) {
+    is_null = false;
+  }
+  return out;
+}
+
 int32_t ComputedAccessibleNode::GetIntAttribute(WebAOMIntAttribute attr,
                                                 bool& is_null) const {
   int32_t out = 0;
diff --git a/third_party/WebKit/Source/core/dom/ComputedAccessibleNode.h b/third_party/WebKit/Source/core/dom/ComputedAccessibleNode.h
index 0a2a074..047247f 100644
--- a/third_party/WebKit/Source/core/dom/ComputedAccessibleNode.h
+++ b/third_party/WebKit/Source/core/dom/ComputedAccessibleNode.h
@@ -68,6 +68,10 @@
   int32_t rowSpan(bool& is_null) const;
   int32_t setSize(bool& is_null) const;
 
+  float valueMax(bool& is_null) const;
+  float valueMin(bool& is_null) const;
+  float valueNow(bool& is_null) const;
+
   const String autocomplete() const;
   const String checked() const;
   const String keyShortcuts() const;
@@ -93,6 +97,7 @@
   void OnUpdateResponse(ScriptPromiseResolver*);
   bool GetBoolAttribute(WebAOMBoolAttribute, bool& is_null) const;
   int32_t GetIntAttribute(WebAOMIntAttribute, bool& is_null) const;
+  float GetFloatAttribute(WebAOMFloatAttribute, bool& is_null) const;
   const String GetStringAttribute(WebAOMStringAttribute) const;
 
   AXID ax_id_;
diff --git a/third_party/WebKit/Source/core/dom/ComputedAccessibleNode.idl b/third_party/WebKit/Source/core/dom/ComputedAccessibleNode.idl
index 1e123a2..af704e6 100644
--- a/third_party/WebKit/Source/core/dom/ComputedAccessibleNode.idl
+++ b/third_party/WebKit/Source/core/dom/ComputedAccessibleNode.idl
@@ -25,6 +25,10 @@
     readonly attribute unsigned long? rowSpan;
     readonly attribute long? setSize;
 
+    readonly attribute double? valueMax;
+    readonly attribute double? valueMin;
+    readonly attribute double? valueNow;
+
     readonly attribute DOMString? autocomplete;
     readonly attribute DOMString? checked;
     readonly attribute DOMString? keyShortcuts;
diff --git a/third_party/WebKit/Source/core/layout/ng/inline/ng_inline_layout_algorithm.cc b/third_party/WebKit/Source/core/layout/ng/inline/ng_inline_layout_algorithm.cc
index 8e74f3a..8818b553 100644
--- a/third_party/WebKit/Source/core/layout/ng/inline/ng_inline_layout_algorithm.cc
+++ b/third_party/WebKit/Source/core/layout/ng/inline/ng_inline_layout_algorithm.cc
@@ -424,51 +424,11 @@
 LayoutUnit NGInlineLayoutAlgorithm::OffsetForTextAlign(
     const NGLineInfo& line_info,
     ETextAlign text_align) const {
-  bool is_base_ltr = IsLtr(line_info.BaseDirection());
-  while (true) {
-    switch (text_align) {
-      case ETextAlign::kLeft:
-      case ETextAlign::kWebkitLeft: {
-        // The direction of the block should determine what happens with wide
-        // lines. In particular with RTL blocks, wide lines should still spill
-        // out to the left.
-        if (is_base_ltr)
-          return LayoutUnit();
-        NGLineAlign align(line_info);
-        return align.space.ClampPositiveToZero();
-      }
-      case ETextAlign::kRight:
-      case ETextAlign::kWebkitRight: {
-        // Wide lines spill out of the block based off direction.
-        // So even if text-align is right, if direction is LTR, wide lines
-        // should overflow out of the right side of the block.
-        NGLineAlign align(line_info);
-        if (align.space > 0 || !is_base_ltr)
-          return align.space;
-        return LayoutUnit();
-      }
-      case ETextAlign::kCenter:
-      case ETextAlign::kWebkitCenter: {
-        NGLineAlign align(line_info);
-        if (is_base_ltr || align.space > 0)
-          return (align.space / 2).ClampNegativeToZero();
-        // In RTL, wide lines should spill out to the left, same as kRight.
-        return align.space;
-      }
-      case ETextAlign::kStart:
-        text_align = is_base_ltr ? ETextAlign::kLeft : ETextAlign::kRight;
-        continue;
-      case ETextAlign::kEnd:
-        text_align = is_base_ltr ? ETextAlign::kRight : ETextAlign::kLeft;
-        continue;
-      case ETextAlign::kJustify:
-        // Justification is applied in earlier phase, see PlaceItems().
-        NOTREACHED();
-        return LayoutUnit();
-    }
-    NOTREACHED();
-    return LayoutUnit();
-  }
+  // Justification is applied in earlier phase, see PlaceItems().
+  DCHECK_NE(text_align, ETextAlign::kJustify);
+
+  return LineOffsetForTextAlign(text_align, line_info.BaseDirection(),
+                                NGLineAlign(line_info).space);
 }
 
 LayoutUnit NGInlineLayoutAlgorithm::ComputeContentSize(
diff --git a/third_party/WebKit/Source/core/layout/ng/ng_block_layout_algorithm.cc b/third_party/WebKit/Source/core/layout/ng/ng_block_layout_algorithm.cc
index 172e1ce4..8327900b 100644
--- a/third_party/WebKit/Source/core/layout/ng/ng_block_layout_algorithm.cc
+++ b/third_party/WebKit/Source/core/layout/ng/ng_block_layout_algorithm.cc
@@ -240,6 +240,7 @@
 }
 
 NGLogicalOffset NGBlockLayoutAlgorithm::CalculateLogicalOffset(
+    NGLayoutInputNode child,
     const NGFragment& fragment,
     const NGBoxStrut& child_margins,
     const WTF::Optional<NGBfcOffset>& known_fragment_offset) {
@@ -252,6 +253,15 @@
   LayoutUnit inline_offset =
       border_scrollbar_padding_.inline_start + child_margins.inline_start;
 
+  if (child.IsInline()) {
+    LayoutUnit offset =
+        LineOffsetForTextAlign(Style().GetTextAlign(), Style().Direction(),
+                               child_available_size_.inline_size);
+    if (IsRtl(Style().Direction()))
+      offset = child_available_size_.inline_size - offset;
+    inline_offset += offset;
+  }
+
   // If we've reached here, both the child and the current layout don't have a
   // BFC offset yet. Children in this situation are always placed at a logical
   // block offset of 0.
@@ -1014,8 +1024,8 @@
   const auto& physical_fragment = *layout_result->PhysicalFragment();
   NGFragment fragment(ConstraintSpace().GetWritingMode(), physical_fragment);
 
-  NGLogicalOffset logical_offset =
-      CalculateLogicalOffset(fragment, child_data.margins, child_bfc_offset);
+  NGLogicalOffset logical_offset = CalculateLogicalOffset(
+      child, fragment, child_data.margins, child_bfc_offset);
 
   if (ConstraintSpace().HasBlockFragmentation()) {
     if (BreakBeforeChild(child, *layout_result, logical_offset.block_offset,
@@ -1191,6 +1201,12 @@
       child_data.bfc_offset_estimate.block_offset +
           layout_result.EndMarginStrut().Sum()};
 
+  if (child.IsInline()) {
+    child_bfc_offset.line_offset +=
+        LineOffsetForTextAlign(Style().GetTextAlign(), Style().Direction(),
+                               child_available_size_.inline_size);
+  }
+
   *empty_block_affected_by_clearance =
       AdjustToClearance(space.ClearanceOffset(), &child_bfc_offset);
 
diff --git a/third_party/WebKit/Source/core/layout/ng/ng_block_layout_algorithm.h b/third_party/WebKit/Source/core/layout/ng/ng_block_layout_algorithm.h
index 1060ac801f..db76cf03 100644
--- a/third_party/WebKit/Source/core/layout/ng/ng_block_layout_algorithm.h
+++ b/third_party/WebKit/Source/core/layout/ng/ng_block_layout_algorithm.h
@@ -196,6 +196,7 @@
   // {@code known_fragment_offset} if the fragment knows it's offset
   // @return Fragment's offset relative to the fragment's parent.
   NGLogicalOffset CalculateLogicalOffset(
+      NGLayoutInputNode child,
       const NGFragment&,
       const NGBoxStrut& child_margins,
       const WTF::Optional<NGBfcOffset>& known_fragment_offset);
diff --git a/third_party/WebKit/Source/core/layout/ng/ng_length_utils.cc b/third_party/WebKit/Source/core/layout/ng/ng_length_utils.cc
index a67243c..1d8a5acb 100644
--- a/third_party/WebKit/Source/core/layout/ng/ng_length_utils.cc
+++ b/third_party/WebKit/Source/core/layout/ng/ng_length_utils.cc
@@ -596,6 +596,47 @@
       available_inline_size - inline_size - margins->inline_start;
 }
 
+LayoutUnit LineOffsetForTextAlign(ETextAlign text_align,
+                                  TextDirection direction,
+                                  LayoutUnit space_left) {
+  bool is_ltr = IsLtr(direction);
+  if (text_align == ETextAlign::kStart || text_align == ETextAlign::kJustify)
+    text_align = is_ltr ? ETextAlign::kLeft : ETextAlign::kRight;
+  else if (text_align == ETextAlign::kEnd)
+    text_align = is_ltr ? ETextAlign::kRight : ETextAlign::kLeft;
+
+  switch (text_align) {
+    case ETextAlign::kLeft:
+    case ETextAlign::kWebkitLeft: {
+      // The direction of the block should determine what happens with wide
+      // lines. In particular with RTL blocks, wide lines should still spill
+      // out to the left.
+      if (is_ltr)
+        return LayoutUnit();
+      return space_left.ClampPositiveToZero();
+    }
+    case ETextAlign::kRight:
+    case ETextAlign::kWebkitRight: {
+      // Wide lines spill out of the block based off direction.
+      // So even if text-align is right, if direction is LTR, wide lines
+      // should overflow out of the right side of the block.
+      if (space_left > LayoutUnit() || !is_ltr)
+        return space_left;
+      return LayoutUnit();
+    }
+    case ETextAlign::kCenter:
+    case ETextAlign::kWebkitCenter: {
+      if (is_ltr || space_left > LayoutUnit())
+        return (space_left / 2).ClampNegativeToZero();
+      // In RTL, wide lines should spill out to the left, same as kRight.
+      return space_left;
+    }
+    default:
+      NOTREACHED();
+      return LayoutUnit();
+  }
+}
+
 LayoutUnit ConstrainByMinMax(LayoutUnit length,
                              Optional<LayoutUnit> min,
                              Optional<LayoutUnit> max) {
diff --git a/third_party/WebKit/Source/core/layout/ng/ng_length_utils.h b/third_party/WebKit/Source/core/layout/ng/ng_length_utils.h
index ac4fce1..aef87a7 100644
--- a/third_party/WebKit/Source/core/layout/ng/ng_length_utils.h
+++ b/third_party/WebKit/Source/core/layout/ng/ng_length_utils.h
@@ -9,6 +9,7 @@
 #include "core/layout/MinMaxSize.h"
 #include "core/layout/ng/geometry/ng_box_strut.h"
 #include "core/layout/ng/geometry/ng_logical_size.h"
+#include "core/style/ComputedStyleConstants.h"
 #include "platform/text/TextDirection.h"
 #include "platform/text/WritingMode.h"
 #include "platform/wtf/Optional.h"
@@ -145,6 +146,12 @@
                                   LayoutUnit inline_size,
                                   NGBoxStrut* margins);
 
+// Calculate the adjustment needed for the line's left position, based on
+// text-align, direction and amount of unused space.
+CORE_EXPORT LayoutUnit LineOffsetForTextAlign(ETextAlign,
+                                              TextDirection,
+                                              LayoutUnit space_left);
+
 CORE_EXPORT LayoutUnit ConstrainByMinMax(LayoutUnit length,
                                          Optional<LayoutUnit> min,
                                          Optional<LayoutUnit> max);
diff --git a/third_party/WebKit/Source/core/paint/PaintLayerScrollableArea.cpp b/third_party/WebKit/Source/core/paint/PaintLayerScrollableArea.cpp
index ad4ab3f..cd8e82e 100644
--- a/third_party/WebKit/Source/core/paint/PaintLayerScrollableArea.cpp
+++ b/third_party/WebKit/Source/core/paint/PaintLayerScrollableArea.cpp
@@ -246,6 +246,8 @@
 }
 
 PlatformChromeClient* PaintLayerScrollableArea::GetChromeClient() const {
+  if (HasBeenDisposed())
+    return nullptr;
   if (Page* page = GetLayoutBox()->GetFrame()->GetPage())
     return &page->GetChromeClient();
   return nullptr;
@@ -713,12 +715,16 @@
 }
 
 bool PaintLayerScrollableArea::ScrollAnimatorEnabled() const {
+  if (HasBeenDisposed())
+    return false;
   if (Settings* settings = GetLayoutBox()->GetFrame()->GetSettings())
     return settings->GetScrollAnimatorEnabled();
   return false;
 }
 
 bool PaintLayerScrollableArea::ShouldSuspendScrollAnimations() const {
+  if (HasBeenDisposed())
+    return true;
   LayoutView* view = GetLayoutBox()->View();
   if (!view)
     return true;
@@ -2114,6 +2120,8 @@
 }
 
 bool PaintLayerScrollableArea::ShouldScrollOnMainThread() const {
+  if (HasBeenDisposed())
+    return true;
   if (LocalFrame* frame = GetLayoutBox()->GetFrame()) {
     if (frame->View()->GetMainThreadScrollingReasons())
       return true;
diff --git a/third_party/WebKit/Source/core/paint/SVGContainerPainter.cpp b/third_party/WebKit/Source/core/paint/SVGContainerPainter.cpp
index 31c1c3a..3affc19 100644
--- a/third_party/WebKit/Source/core/paint/SVGContainerPainter.cpp
+++ b/third_party/WebKit/Source/core/paint/SVGContainerPainter.cpp
@@ -54,10 +54,15 @@
         if (!fragment)
           return;
         const auto* properties = fragment->PaintProperties();
-        DCHECK(properties && properties->OverflowClip());
-        scoped_paint_chunk_properties.emplace(
-            paint_info.context.GetPaintController(), properties->OverflowClip(),
-            layout_svg_container_, paint_info.DisplayItemTypeForClipping());
+        // TODO(crbug.com/814815): The condition should be a DCHECK, but for now
+        // we may paint the object for filters during PrePaint before the
+        // properties are ready.
+        if (properties && properties->OverflowClip()) {
+          scoped_paint_chunk_properties.emplace(
+              paint_info.context.GetPaintController(),
+              properties->OverflowClip(), layout_svg_container_,
+              paint_info.DisplayItemTypeForClipping());
+        }
       } else {
         FloatRect viewport =
             layout_svg_container_.LocalToSVGParentTransform().Inverse().MapRect(
diff --git a/third_party/WebKit/Source/core/paint/SVGForeignObjectPainter.cpp b/third_party/WebKit/Source/core/paint/SVGForeignObjectPainter.cpp
index bd1cdf2..19098ea 100644
--- a/third_party/WebKit/Source/core/paint/SVGForeignObjectPainter.cpp
+++ b/third_party/WebKit/Source/core/paint/SVGForeignObjectPainter.cpp
@@ -57,10 +57,15 @@
       if (!fragment)
         return;
       const auto* properties = fragment->PaintProperties();
-      DCHECK(properties && properties->OverflowClip());
-      scoped_paint_chunk_properties.emplace(
-          paint_info.context.GetPaintController(), properties->OverflowClip(),
-          layout_svg_foreign_object_, paint_info.DisplayItemTypeForClipping());
+      // TODO(crbug.com/814815): The condition should be a DCHECK, but for now
+      // we may paint the object for filters during PrePaint before the
+      // properties are ready.
+      if (properties && properties->OverflowClip()) {
+        scoped_paint_chunk_properties.emplace(
+            paint_info.context.GetPaintController(), properties->OverflowClip(),
+            layout_svg_foreign_object_,
+            paint_info.DisplayItemTypeForClipping());
+      }
     } else {
       clip_recorder.emplace(paint_info_before_filtering.context,
                             layout_svg_foreign_object_,
diff --git a/third_party/WebKit/Source/modules/accessibility/AXObjectCacheImpl.cpp b/third_party/WebKit/Source/modules/accessibility/AXObjectCacheImpl.cpp
index 713e0fd..8c38e05 100644
--- a/third_party/WebKit/Source/modules/accessibility/AXObjectCacheImpl.cpp
+++ b/third_party/WebKit/Source/modules/accessibility/AXObjectCacheImpl.cpp
@@ -306,7 +306,13 @@
 }
 
 static bool NodeHasGridRole(Node* node) {
-  return NodeHasRole(node, "grid") || NodeHasRole(node, "treegrid");
+  return NodeHasRole(node, "grid") || NodeHasRole(node, "treegrid") ||
+         NodeHasRole(node, "table");
+}
+
+static bool NodeHasCellRole(Node* node) {
+  return NodeHasRole(node, "cell") || NodeHasRole(node, "gridcell") ||
+         NodeHasRole(node, "columnheader") || NodeHasRole(node, "rowheader");
 }
 
 AXObject* AXObjectCacheImpl::CreateFromRenderer(LayoutObject* layout_object) {
@@ -326,8 +332,7 @@
     return AXARIAGrid::Create(layout_object, *this);
   if (NodeHasRole(node, "row"))
     return AXARIAGridRow::Create(layout_object, *this);
-  if (NodeHasRole(node, "gridcell") || NodeHasRole(node, "columnheader") ||
-      NodeHasRole(node, "rowheader"))
+  if (NodeHasCellRole(node))
     return AXARIAGridCell::Create(layout_object, *this);
 
   // media controls
diff --git a/third_party/WebKit/Source/modules/canvas/canvas2d/CanvasRenderingContext2D.cpp b/third_party/WebKit/Source/modules/canvas/canvas2d/CanvasRenderingContext2D.cpp
index 1e89cab..1f8a3c6c 100644
--- a/third_party/WebKit/Source/modules/canvas/canvas2d/CanvasRenderingContext2D.cpp
+++ b/third_party/WebKit/Source/modules/canvas/canvas2d/CanvasRenderingContext2D.cpp
@@ -654,8 +654,13 @@
       box->AbsoluteToLocal(FloatPoint(location), kUseTransforms);
   if (box->HasBorderOrPadding())
     local_pos.Move(-box->ContentBoxOffset());
-  local_pos.Scale(canvas()->width() / box->ContentWidth(),
-                  canvas()->height() / box->ContentHeight());
+  float scaleWidth = box->ContentWidth().ToFloat() == 0.0f
+                         ? 1.0f
+                         : canvas()->width() / box->ContentWidth();
+  float scaleHeight = box->ContentHeight().ToFloat() == 0.0f
+                          ? 1.0f
+                          : canvas()->height() / box->ContentHeight();
+  local_pos.Scale(scaleWidth, scaleHeight);
 
   HitRegion* hit_region = HitRegionAtPoint(local_pos);
   if (hit_region) {
diff --git a/third_party/WebKit/Source/modules/credentialmanager/CredentialManagerTypeConverters.cpp b/third_party/WebKit/Source/modules/credentialmanager/CredentialManagerTypeConverters.cpp
index a60f392..f1fa875 100644
--- a/third_party/WebKit/Source/modules/credentialmanager/CredentialManagerTypeConverters.cpp
+++ b/third_party/WebKit/Source/modules/credentialmanager/CredentialManagerTypeConverters.cpp
@@ -112,8 +112,6 @@
       return CredentialManagerError::PENDING_REQUEST;
     case webauth::mojom::blink::AuthenticatorStatus::INVALID_DOMAIN:
       return CredentialManagerError::INVALID_DOMAIN;
-    case webauth::mojom::blink::AuthenticatorStatus::TIMED_OUT:
-      return CredentialManagerError::TIMED_OUT;
     case webauth::mojom::blink::AuthenticatorStatus::NOT_IMPLEMENTED:
       return CredentialManagerError::NOT_IMPLEMENTED;
     case webauth::mojom::blink::AuthenticatorStatus::SUCCESS:
diff --git a/third_party/WebKit/Source/modules/credentialmanager/CredentialsContainer.cpp b/third_party/WebKit/Source/modules/credentialmanager/CredentialsContainer.cpp
index 213f26d..962ded7 100644
--- a/third_party/WebKit/Source/modules/credentialmanager/CredentialsContainer.cpp
+++ b/third_party/WebKit/Source/modules/credentialmanager/CredentialsContainer.cpp
@@ -237,16 +237,16 @@
       return DOMException::Create(kNotSupportedError,
                                   "The password store is unavailable.");
     case CredentialManagerError::NOT_ALLOWED:
-      return DOMException::Create(kNotAllowedError,
-                                  "The operation is not allowed.");
+      return DOMException::Create(
+          kNotAllowedError,
+          "The operation either timed out or was not allowed. See: "
+          "https://w3c.github.io/webauthn/#sec-assertion-privacy.");
     case CredentialManagerError::NOT_SUPPORTED:
       return DOMException::Create(
           kNotSupportedError,
           "Parameters for this operation are not supported.");
     case CredentialManagerError::INVALID_DOMAIN:
       return DOMException::Create(kSecurityError, "This is an invalid domain.");
-    case CredentialManagerError::TIMED_OUT:
-      return DOMException::Create(kNotAllowedError, "Operation timed out.");
     case CredentialManagerError::NOT_IMPLEMENTED:
       return DOMException::Create(kNotSupportedError, "Not implemented");
     case CredentialManagerError::UNKNOWN:
diff --git a/third_party/WebKit/Source/modules/notifications/NotificationManager.cpp b/third_party/WebKit/Source/modules/notifications/NotificationManager.cpp
index ddc660d..f1884cb 100644
--- a/third_party/WebKit/Source/modules/notifications/NotificationManager.cpp
+++ b/third_party/WebKit/Source/modules/notifications/NotificationManager.cpp
@@ -10,7 +10,9 @@
 #include "core/frame/LocalFrame.h"
 #include "modules/notifications/Notification.h"
 #include "modules/permissions/PermissionUtils.h"
+#include "platform/Histogram.h"
 #include "platform/bindings/ScriptState.h"
+#include "platform/heap/Persistent.h"
 #include "platform/weborigin/SecurityOrigin.h"
 #include "platform/wtf/Functional.h"
 #include "public/platform/InterfaceProvider.h"
@@ -19,6 +21,7 @@
 #include "public/platform/modules/notifications/notification.mojom-blink.h"
 #include "public/platform/modules/permissions/permission.mojom-blink.h"
 #include "public/platform/modules/permissions/permission_status.mojom-blink.h"
+#include "public/platform/modules/serviceworker/WebServiceWorkerRegistration.h"
 #include "services/service_manager/public/cpp/interface_provider.h"
 
 namespace blink {
@@ -124,6 +127,42 @@
   GetNotificationService()->CloseNonPersistentNotification(token);
 }
 
+void NotificationManager::DisplayPersistentNotification(
+    blink::WebServiceWorkerRegistration* service_worker_registration,
+    const blink::WebNotificationData& notification_data,
+    std::unique_ptr<blink::WebNotificationResources> notification_resources,
+    std::unique_ptr<blink::WebNotificationShowCallbacks> callbacks) {
+  DCHECK(notification_resources);
+  DCHECK_EQ(notification_data.actions.size(),
+            notification_resources->action_icons.size());
+
+  DEFINE_THREAD_SAFE_STATIC_LOCAL(
+      CustomCountHistogram, notification_data_size_histogram,
+      ("Notifications.AuthorDataSize", 1, 1000, 50));
+  notification_data_size_histogram.Count(notification_data.data.size());
+
+  GetNotificationService()->DisplayPersistentNotification(
+      service_worker_registration->RegistrationId(), notification_data,
+      *notification_resources,
+      WTF::Bind(&NotificationManager::DidDisplayPersistentNotification,
+                WrapPersistent(this), std::move(callbacks)));
+}
+
+void NotificationManager::DidDisplayPersistentNotification(
+    std::unique_ptr<blink::WebNotificationShowCallbacks> callbacks,
+    mojom::blink::PersistentNotificationError error) {
+  switch (error) {
+    case mojom::blink::PersistentNotificationError::NONE:
+      callbacks->OnSuccess();
+      return;
+    case mojom::blink::PersistentNotificationError::INTERNAL_ERROR:
+    case mojom::blink::PersistentNotificationError::PERMISSION_DENIED:
+      callbacks->OnError();
+      return;
+  }
+  NOTREACHED();
+}
+
 const mojom::blink::NotificationServicePtr&
 NotificationManager::GetNotificationService() {
   if (!notification_service_) {
diff --git a/third_party/WebKit/Source/modules/notifications/NotificationManager.h b/third_party/WebKit/Source/modules/notifications/NotificationManager.h
index b6e205a1..ff9af29 100644
--- a/third_party/WebKit/Source/modules/notifications/NotificationManager.h
+++ b/third_party/WebKit/Source/modules/notifications/NotificationManager.h
@@ -10,6 +10,7 @@
 #include "platform/wtf/text/WTFString.h"
 #include "public/platform/modules/notifications/notification_service.mojom-blink.h"
 #include "public/platform/modules/permissions/permission.mojom-blink.h"
+#include "third_party/WebKit/public/platform/modules/notifications/WebNotificationManager.h"
 
 namespace blink {
 
@@ -58,11 +59,22 @@
   // Closes the notification that was most recently displayed with this token.
   void CloseNonPersistentNotification(const String& token);
 
+  // Shows a notification from a service worker.
+  void DisplayPersistentNotification(
+      blink::WebServiceWorkerRegistration*,
+      const blink::WebNotificationData&,
+      std::unique_ptr<blink::WebNotificationResources>,
+      std::unique_ptr<blink::WebNotificationShowCallbacks>);
+
   virtual void Trace(blink::Visitor*);
 
  private:
   explicit NotificationManager(ExecutionContext&);
 
+  void DidDisplayPersistentNotification(
+      std::unique_ptr<blink::WebNotificationShowCallbacks>,
+      mojom::blink::PersistentNotificationError);
+
   // Returns an initialized NotificationServicePtr. A connection will be
   // established the first time this method is called.
   const mojom::blink::NotificationServicePtr& GetNotificationService();
diff --git a/third_party/WebKit/Source/modules/notifications/ServiceWorkerRegistrationNotifications.cpp b/third_party/WebKit/Source/modules/notifications/ServiceWorkerRegistrationNotifications.cpp
index 53a4ed259..14262db 100644
--- a/third_party/WebKit/Source/modules/notifications/ServiceWorkerRegistrationNotifications.cpp
+++ b/third_party/WebKit/Source/modules/notifications/ServiceWorkerRegistrationNotifications.cpp
@@ -189,7 +189,10 @@
   DCHECK(loaders_.Contains(loader));
 
   if (RuntimeEnabledFeatures::NotificationsWithMojoEnabled()) {
-    // TODO(https://crbug.com/796991): Implement this via mojo.
+    NotificationManager::From(GetExecutionContext())
+        ->DisplayPersistentNotification(registration_->WebRegistration(), data,
+                                        loader->GetResources(),
+                                        std::move(callbacks));
   } else {
     WebNotificationManager* notification_manager =
         Platform::Current()->GetWebNotificationManager();
diff --git a/third_party/WebKit/Source/modules/payments/PaymentInstruments.cpp b/third_party/WebKit/Source/modules/payments/PaymentInstruments.cpp
index 2236a16..aedda2f 100644
--- a/third_party/WebKit/Source/modules/payments/PaymentInstruments.cpp
+++ b/third_party/WebKit/Source/modules/payments/PaymentInstruments.cpp
@@ -45,10 +45,13 @@
                                             "Storage operation is failed"));
       return true;
     case payments::mojom::blink::PaymentHandlerStatus::
-        FETCH_INSTRUMENT_ICON_FAILED:
-      resolver->Reject(DOMException::Create(
-          kNotFoundError, "Fetch or decode instrument icon failed"));
+        FETCH_INSTRUMENT_ICON_FAILED: {
+      ScriptState::Scope scope(resolver->GetScriptState());
+      resolver->Reject(V8ThrowException::CreateTypeError(
+          resolver->GetScriptState()->GetIsolate(),
+          "Fetch or decode instrument icon failed"));
       return true;
+    }
     case payments::mojom::blink::PaymentHandlerStatus::
         FETCH_PAYMENT_APP_INFO_FAILED:
       // FETCH_PAYMENT_APP_INFO_FAILED indicates everything works well except
diff --git a/third_party/WebKit/Source/platform/BUILD.gn b/third_party/WebKit/Source/platform/BUILD.gn
index 767ff39..9808c1c 100644
--- a/third_party/WebKit/Source/platform/BUILD.gn
+++ b/third_party/WebKit/Source/platform/BUILD.gn
@@ -799,6 +799,8 @@
     "fonts/WebFontDecoder.cpp",
     "fonts/WebFontDecoder.h",
     "fonts/WebFontRenderStyle.cpp",
+    "fonts/WebFontTypefaceFactory.cpp",
+    "fonts/WebFontTypefaceFactory.h",
     "fonts/android/FontCacheAndroid.cpp",
     "fonts/linux/FontCacheLinux.cpp",
     "fonts/mac/CoreTextVariationsSupport.cpp",
@@ -807,6 +809,8 @@
     "fonts/mac/FontFamilyMatcherMac.h",
     "fonts/mac/FontFamilyMatcherMac.mm",
     "fonts/mac/FontPlatformDataMac.mm",
+    "fonts/opentype/FontFormatCheck.cpp",
+    "fonts/opentype/FontFormatCheck.h",
     "fonts/opentype/FontSettings.cpp",
     "fonts/opentype/FontSettings.h",
     "fonts/opentype/OpenTypeCapsSupport.cpp",
@@ -815,8 +819,6 @@
     "fonts/opentype/OpenTypeTypes.h",
     "fonts/opentype/OpenTypeVerticalData.cpp",
     "fonts/opentype/OpenTypeVerticalData.h",
-    "fonts/opentype/VariableFontCheck.cpp",
-    "fonts/opentype/VariableFontCheck.h",
     "fonts/shaping/CachingWordShapeIterator.cpp",
     "fonts/shaping/CachingWordShapeIterator.h",
     "fonts/shaping/CachingWordShaper.cpp",
diff --git a/third_party/WebKit/Source/platform/bindings/ScriptWrappableMarkingVisitor.h b/third_party/WebKit/Source/platform/bindings/ScriptWrappableMarkingVisitor.h
index 03ad2ec1..fe437fc6 100644
--- a/third_party/WebKit/Source/platform/bindings/ScriptWrappableMarkingVisitor.h
+++ b/third_party/WebKit/Source/platform/bindings/ScriptWrappableMarkingVisitor.h
@@ -9,7 +9,6 @@
 #include "platform/bindings/ScriptWrappableVisitor.h"
 #include "platform/heap/HeapPage.h"
 #include "platform/heap/ThreadingTraits.h"
-#include "platform/heap/VisitorImpl.h"
 #include "platform/wtf/Deque.h"
 #include "platform/wtf/Vector.h"
 #include "v8/include/v8.h"
diff --git a/third_party/WebKit/Source/platform/bindings/ScriptWrappableVisitor.h b/third_party/WebKit/Source/platform/bindings/ScriptWrappableVisitor.h
index b959d8f7..6335b21 100644
--- a/third_party/WebKit/Source/platform/bindings/ScriptWrappableVisitor.h
+++ b/third_party/WebKit/Source/platform/bindings/ScriptWrappableVisitor.h
@@ -9,7 +9,6 @@
 #include "platform/bindings/TraceWrapperBase.h"
 #include "platform/heap/HeapPage.h"
 #include "platform/heap/ThreadingTraits.h"
-#include "platform/heap/VisitorImpl.h"
 #include "platform/wtf/Deque.h"
 #include "platform/wtf/Vector.h"
 #include "v8/include/v8.h"
diff --git a/third_party/WebKit/Source/platform/fonts/FontCustomPlatformData.cpp b/third_party/WebKit/Source/platform/fonts/FontCustomPlatformData.cpp
index 5fa9b4b..abfa801 100644
--- a/third_party/WebKit/Source/platform/fonts/FontCustomPlatformData.cpp
+++ b/third_party/WebKit/Source/platform/fonts/FontCustomPlatformData.cpp
@@ -33,24 +33,18 @@
 #include "platform/fonts/FontCustomPlatformData.h"
 
 #include "build/build_config.h"
-#include "platform/Histogram.h"
 #include "platform/LayoutTestSupport.h"
 #include "platform/SharedBuffer.h"
 #include "platform/fonts/FontCache.h"
 #include "platform/fonts/FontPlatformData.h"
 #include "platform/fonts/WebFontDecoder.h"
-#if defined(OS_MACOSX)
-#include "platform/fonts/mac/CoreTextVariationsSupport.h"
-#endif
+#include "platform/fonts/WebFontTypefaceFactory.h"
+#include "platform/fonts/opentype/FontFormatCheck.h"
 #include "platform/fonts/opentype/FontSettings.h"
-#include "platform/fonts/opentype/VariableFontCheck.h"
 #include "platform/graphics/paint/PaintTypeface.h"
+#include "platform/wtf/PtrUtil.h"
 #include "third_party/skia/include/core/SkStream.h"
 #include "third_party/skia/include/core/SkTypeface.h"
-#if defined(OS_WIN) || defined(OS_MACOSX)
-#include "third_party/skia/include/ports/SkFontMgr_empty.h"
-#endif
-#include "platform/wtf/PtrUtil.h"
 
 namespace blink {
 
@@ -80,18 +74,7 @@
   // now, going with a reasonable upper limit. Deduplication is
   // handled by Skia with priority given to the last occuring
   // assignment.
-  if (VariableFontCheck::IsVariableFont(base_typeface_.get())) {
-#if defined(OS_WIN)
-    sk_sp<SkFontMgr> fm(SkFontMgr_New_Custom_Empty());
-#elif defined(OS_MACOSX)
-    sk_sp<SkFontMgr> fm;
-    if (CoreTextVersionSupportsVariations())
-      fm = SkFontMgr::RefDefault();
-    else
-      fm = SkFontMgr_New_Custom_Empty();
-#else
-    sk_sp<SkFontMgr> fm(SkFontMgr::RefDefault());
-#endif
+  if (FontFormatCheck::IsVariableFont(base_typeface_)) {
     Vector<SkFontArguments::Axis, 0> axes;
 
     SkFontArguments::Axis weight_axis = {
@@ -121,23 +104,20 @@
       }
     }
 
-    sk_sp<SkTypeface> sk_variation_font(fm->makeFromStream(
-        base_typeface_->openStream(nullptr)->duplicate(),
-        SkFontArguments().setAxes(axes.data(), axes.size())));
+    sk_sp<SkTypeface> sk_variation_font(
+        WebFontTypefaceFactory::FontManagerForVariations()->makeFromStream(
+            base_typeface_->openStream(nullptr)->duplicate(),
+            SkFontArguments().setAxes(axes.data(), axes.size())));
 
     if (sk_variation_font) {
-      ReportWebFontInstantiationResult(kSuccessVariableWebFont);
       return_typeface = sk_variation_font;
     } else {
-      ReportWebFontInstantiationResult(kErrorInstantiatingVariableFont);
       SkString family_name;
       base_typeface_->getFamilyName(&family_name);
       // TODO: Surface this as a console message?
       LOG(ERROR) << "Unable for apply variation axis properties for font: "
                  << family_name.c_str();
     }
-  } else {
-    ReportWebFontInstantiationResult(kSuccessConventionalWebFont);
   }
 
   // TODO(vmpstr): Handle web fonts PaintTypefaces.
@@ -161,14 +141,6 @@
       new FontCustomPlatformData(std::move(typeface), decoder.DecodedSize()));
 }
 
-void FontCustomPlatformData::ReportWebFontInstantiationResult(
-    WebFontInstantiationResult result) {
-  DEFINE_THREAD_SAFE_STATIC_LOCAL(
-      EnumerationHistogram, web_font_variable_fonts_ratio,
-      ("Blink.Fonts.VariableFontsRatio", kMaxWebFontInstantiationResult));
-  web_font_variable_fonts_ratio.Count(result);
-}
-
 bool FontCustomPlatformData::SupportsFormat(const String& format) {
   // Support relevant format specifiers from
   // https://drafts.csswg.org/css-fonts-4/#src-desc
diff --git a/third_party/WebKit/Source/platform/fonts/FontCustomPlatformData.h b/third_party/WebKit/Source/platform/fonts/FontCustomPlatformData.h
index 23462b8e..a1e4551 100644
--- a/third_party/WebKit/Source/platform/fonts/FontCustomPlatformData.h
+++ b/third_party/WebKit/Source/platform/fonts/FontCustomPlatformData.h
@@ -73,16 +73,6 @@
   static bool SupportsFormat(const String&);
 
  private:
-  // These values are written to logs.  New enum values can be added, but
-  // existing enums must never be renumbered or deleted and reused.
-  enum WebFontInstantiationResult {
-    kErrorInstantiatingVariableFont = 0,
-    kSuccessConventionalWebFont = 1,
-    kSuccessVariableWebFont = 2,
-    kMaxWebFontInstantiationResult = 3
-  };
-
-  static void ReportWebFontInstantiationResult(WebFontInstantiationResult);
   FontCustomPlatformData(sk_sp<SkTypeface>, size_t data_size);
   sk_sp<SkTypeface> base_typeface_;
   size_t data_size_;
diff --git a/third_party/WebKit/Source/platform/fonts/WebFontDecoder.cpp b/third_party/WebKit/Source/platform/fonts/WebFontDecoder.cpp
index 8565b91..953c62d 100644
--- a/third_party/WebKit/Source/platform/fonts/WebFontDecoder.cpp
+++ b/third_party/WebKit/Source/platform/fonts/WebFontDecoder.cpp
@@ -34,6 +34,7 @@
 #include "platform/Histogram.h"
 #include "platform/SharedBuffer.h"
 #include "platform/fonts/FontCache.h"
+#include "platform/fonts/WebFontTypefaceFactory.h"
 #include "platform/graphics/skia/SkiaUtils.h"
 #include "platform/instrumentation/tracing/TraceEvent.h"
 #include "platform/wtf/Time.h"
@@ -96,6 +97,8 @@
   const uint32_t kCblcTag = OTS_TAG('C', 'B', 'L', 'C');
   const uint32_t kColrTag = OTS_TAG('C', 'O', 'L', 'R');
   const uint32_t kCpalTag = OTS_TAG('C', 'P', 'A', 'L');
+  const uint32_t kCff2Tag = OTS_TAG('C', 'F', 'F', '2');
+  const uint32_t kSbixTag = OTS_TAG('s', 'b', 'i', 'x');
 #if HB_VERSION_ATLEAST(1, 0, 0)
   const uint32_t kGdefTag = OTS_TAG('G', 'D', 'E', 'F');
   const uint32_t kGposTag = OTS_TAG('G', 'P', 'O', 'S');
@@ -120,6 +123,8 @@
     // Windows Color Emoji Tables
     case kColrTag:
     case kCpalTag:
+    case kCff2Tag:
+    case kSbixTag:
 #if HB_VERSION_ATLEAST(1, 0, 0)
     // Let HarfBuzz handle how to deal with broken tables.
     case kAvarTag:
@@ -209,24 +214,18 @@
   RecordDecodeSpeedHistogram(data, buffer->size(), CurrentTime() - start,
                              decoded_length);
 
-  // TODO(fmalita): we can avoid this copy by processing into a
-  // SkDynamicMemoryWStream-backed OTSStream.
   sk_sp<SkData> sk_data = SkData::MakeWithCopy(output.get(), decoded_length);
-  std::unique_ptr<SkStreamAsset> stream(new SkMemoryStream(sk_data));
-#if defined(OS_WIN)
-  sk_sp<SkTypeface> typeface(
-      FontCache::GetFontCache()->FontManager()->makeFromStream(
-          std::move(stream)));
-#else
-  sk_sp<SkTypeface> typeface = SkTypeface::MakeFromStream(stream.release());
-#endif
-  if (!typeface) {
-    SetErrorString("Not a valid font data");
+
+  sk_sp<SkTypeface> new_typeface;
+
+  if (!WebFontTypefaceFactory::CreateTypeface(sk_data, new_typeface)) {
+    SetErrorString("Unable to instantiate font face from font data.");
     return nullptr;
   }
 
   decoded_size_ = decoded_length;
-  return typeface;
+
+  return new_typeface;
 }
 
 }  // namespace blink
diff --git a/third_party/WebKit/Source/platform/fonts/WebFontDecoder.h b/third_party/WebKit/Source/platform/fonts/WebFontDecoder.h
index bb3f40e2..484d2232 100644
--- a/third_party/WebKit/Source/platform/fonts/WebFontDecoder.h
+++ b/third_party/WebKit/Source/platform/fonts/WebFontDecoder.h
@@ -34,7 +34,8 @@
 #include "platform/wtf/Allocator.h"
 #include "platform/wtf/text/WTFString.h"
 #include "third_party/skia/include/core/SkRefCnt.h"
-#include "third_party/skia/include/core/SkTypeface.h"
+
+class SkTypeface;
 
 namespace blink {
 
diff --git a/third_party/WebKit/Source/platform/fonts/WebFontTypefaceFactory.cpp b/third_party/WebKit/Source/platform/fonts/WebFontTypefaceFactory.cpp
new file mode 100644
index 0000000..400d51a
--- /dev/null
+++ b/third_party/WebKit/Source/platform/fonts/WebFontTypefaceFactory.cpp
@@ -0,0 +1,116 @@
+// 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 "platform/fonts/WebFontTypefaceFactory.h"
+
+#include "platform/Histogram.h"
+#include "platform/fonts/FontCache.h"
+#include "platform/fonts/opentype/FontFormatCheck.h"
+#include "platform/wtf/Assertions.h"
+#include "third_party/skia/include/core/SkStream.h"
+#include "third_party/skia/include/core/SkTypeface.h"
+#if defined(OS_WIN) || defined(OS_MACOSX)
+#include "third_party/skia/include/ports/SkFontMgr_empty.h"
+#endif
+#if defined(OS_MACOSX)
+#include "platform/fonts/mac/CoreTextVariationsSupport.h"
+#endif
+
+namespace blink {
+
+bool WebFontTypefaceFactory::CreateTypeface(sk_sp<SkData> sk_data,
+                                            sk_sp<SkTypeface>& typeface) {
+  CHECK(!typeface);
+
+  FontFormatCheck format_check(sk_data);
+
+  std::unique_ptr<SkStreamAsset> stream(new SkMemoryStream(sk_data));
+
+  if (!format_check.IsVariableFont() && !format_check.IsCbdtCblcColorFont() &&
+      !format_check.IsCff2OutlineFont() && !format_check.IsSbixColorFont()) {
+    typeface = DefaultFontManager()->makeFromStream(std::move(stream));
+    if (typeface) {
+      ReportWebFontInstantiationResult(kSuccessConventionalWebFont);
+      return true;
+    }
+    // Not UMA reporting general decoding errors as these are already recorded
+    // as kPackageFormatUnknown in FontResource.cpp.
+    return false;
+  }
+
+  if (format_check.IsCbdtCblcColorFont()) {
+    typeface = FreeTypeFontManager()->makeFromStream(std::move(stream));
+    if (typeface)
+      ReportWebFontInstantiationResult(kSuccessCbdtCblcColorFont);
+  }
+
+  if (format_check.IsSbixColorFont()) {
+    typeface = FontManagerForSbix()->makeFromStream(std::move(stream));
+    if (typeface) {
+      ReportWebFontInstantiationResult(kSuccessSbixFont);
+    }
+  }
+
+  if (format_check.IsCff2OutlineFont()) {
+    typeface = FreeTypeFontManager()->makeFromStream(std::move(stream));
+    if (typeface)
+      ReportWebFontInstantiationResult(kSuccessCff2Font);
+  }
+
+  // Variable CFF2 fonts must go through FreeType.
+  if (format_check.IsVariableFont() && !format_check.IsCff2OutlineFont()) {
+    typeface = FontManagerForVariations()->makeFromStream(std::move(stream));
+    if (typeface)
+      ReportWebFontInstantiationResult(kSuccessVariableWebFont);
+    else
+      ReportWebFontInstantiationResult(kErrorInstantiatingVariableFont);
+  }
+
+  return true;
+}
+
+sk_sp<SkFontMgr> WebFontTypefaceFactory::FontManagerForVariations() {
+#if defined(OS_WIN)
+  return FreeTypeFontManager();
+#else
+#if defined(OS_MACOSX)
+  if (!CoreTextVersionSupportsVariations())
+    return FreeTypeFontManager();
+#endif
+  return DefaultFontManager();
+#endif
+}
+
+sk_sp<SkFontMgr> WebFontTypefaceFactory::FontManagerForSbix() {
+#if defined(OS_MACOSX)
+  return DefaultFontManager();
+#endif
+  return FreeTypeFontManager();
+}
+
+sk_sp<SkFontMgr> WebFontTypefaceFactory::DefaultFontManager() {
+#if defined(OS_WIN)
+  return FontCache::GetFontCache()->FontManager();
+#else
+  return sk_sp<SkFontMgr>(SkFontMgr::RefDefault());
+#endif
+}
+
+sk_sp<SkFontMgr> WebFontTypefaceFactory::FreeTypeFontManager() {
+#if defined(OS_WIN) || defined(OS_MACOSX)
+  return sk_sp<SkFontMgr>(SkFontMgr_New_Custom_Empty());
+#else
+  return DefaultFontManager();
+#endif
+}
+
+void WebFontTypefaceFactory::ReportWebFontInstantiationResult(
+    WebFontInstantiationResult result) {
+  DEFINE_THREAD_SAFE_STATIC_LOCAL(
+      EnumerationHistogram, web_font_variable_fonts_ratio,
+      ("Blink.Fonts.VariableFontsRatio", kMaxWebFontInstantiationResult));
+  web_font_variable_fonts_ratio.Count(result);
+}
+
+}  // namespace blink
diff --git a/third_party/WebKit/Source/platform/fonts/WebFontTypefaceFactory.h b/third_party/WebKit/Source/platform/fonts/WebFontTypefaceFactory.h
new file mode 100644
index 0000000..e8bbbac
--- /dev/null
+++ b/third_party/WebKit/Source/platform/fonts/WebFontTypefaceFactory.h
@@ -0,0 +1,51 @@
+// 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 WebFontTypefaceFactory_h
+#define WebFontTypefaceFactory_h
+
+#include "third_party/skia/include/ports/SkFontMgr.h"
+
+#include "build/build_config.h"
+#if defined(OS_WIN) || defined(OS_MACOSX)
+#include "third_party/skia/include/ports/SkFontMgr_empty.h"
+#endif
+
+namespace blink {
+
+// Decides which Skia Fontmanager to use for instantiating a web font. In the
+// regular case, this would be default font manager used for the platform.
+// However, for variable fonts, color bitmap font formats and CFF2 fonts we want
+// to use FreeType on Windows and Mac.
+class WebFontTypefaceFactory {
+ public:
+  static bool CreateTypeface(const sk_sp<SkData>, sk_sp<SkTypeface>&);
+
+  // TODO(drott): This should be going away in favor of a new API on SkTypeface:
+  // https://bugs.chromium.org/p/skia/issues/detail?id=7121
+  static sk_sp<SkFontMgr> FontManagerForVariations();
+  static sk_sp<SkFontMgr> FontManagerForSbix();
+
+ private:
+  // These values are written to logs.  New enum values can be added, but
+  // existing enums must never be renumbered or deleted and reused.
+  enum WebFontInstantiationResult {
+    kErrorInstantiatingVariableFont = 0,
+    kSuccessConventionalWebFont = 1,
+    kSuccessVariableWebFont = 2,
+    kSuccessCbdtCblcColorFont = 3,
+    kSuccessCff2Font = 4,
+    kSuccessSbixFont = 5,
+    kMaxWebFontInstantiationResult = 6
+  };
+
+  static sk_sp<SkFontMgr> DefaultFontManager();
+  static sk_sp<SkFontMgr> FreeTypeFontManager();
+
+  static void ReportWebFontInstantiationResult(WebFontInstantiationResult);
+};
+
+}  // namespace blink
+
+#endif  // WebFontTypefaceFactory_h
diff --git a/third_party/WebKit/Source/platform/fonts/opentype/FontFormatCheck.cpp b/third_party/WebKit/Source/platform/fonts/opentype/FontFormatCheck.cpp
new file mode 100644
index 0000000..8b0f2d1a
--- /dev/null
+++ b/third_party/WebKit/Source/platform/fonts/opentype/FontFormatCheck.cpp
@@ -0,0 +1,56 @@
+// 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 "platform/fonts/opentype/FontFormatCheck.h"
+
+#include "SkTypeface.h"
+#include "platform/wtf/Vector.h"
+
+// Include HarfBuzz to have a cross-platform way to retrieve table tags without
+// having to rely on the platform being able to instantiate this font format.
+#include <hb.h>
+
+namespace blink {
+
+FontFormatCheck::FontFormatCheck(sk_sp<SkData> sk_data) {
+  std::unique_ptr<hb_blob_t, std::function<void(hb_blob_t*)>> font_blob(
+      hb_blob_create(reinterpret_cast<const char*>(sk_data->bytes()),
+                     sk_data->size(), HB_MEMORY_MODE_READONLY, nullptr,
+                     nullptr),
+      [](hb_blob_t* blob) { hb_blob_destroy(blob); });
+  std::unique_ptr<hb_face_t, std::function<void(hb_face_t*)>> face(
+      hb_face_create(font_blob.get(), 0),
+      [](hb_face_t* face) { hb_face_destroy(face); });
+
+  unsigned table_count = 0;
+  table_count = hb_face_get_table_tags(face.get(), 0, nullptr, nullptr);
+  table_tags_.resize(table_count);
+  if (!hb_face_get_table_tags(face.get(), 0, &table_count, table_tags_.data()))
+    table_tags_.resize(0);
+}
+
+bool FontFormatCheck::IsVariableFont() {
+  return table_tags_.size() && table_tags_.Contains(HB_TAG('f', 'v', 'a', 'r'));
+}
+
+bool FontFormatCheck::IsCbdtCblcColorFont() {
+  return table_tags_.size() &&
+         table_tags_.Contains(HB_TAG('C', 'B', 'D', 'T')) &&
+         table_tags_.Contains(HB_TAG('C', 'B', 'L', 'C'));
+}
+
+bool FontFormatCheck::IsSbixColorFont() {
+  return table_tags_.size() && table_tags_.Contains(HB_TAG('s', 'b', 'i', 'x'));
+}
+
+bool FontFormatCheck::IsCff2OutlineFont() {
+  return table_tags_.size() && table_tags_.Contains(HB_TAG('C', 'F', 'F', '2'));
+}
+
+bool FontFormatCheck::IsVariableFont(sk_sp<SkTypeface> typeface) {
+  return typeface->getTableSize(
+      SkFontTableTag(SkSetFourByteTag('f', 'v', 'a', 'r')));
+}
+
+}  // namespace blink
diff --git a/third_party/WebKit/Source/platform/fonts/opentype/FontFormatCheck.h b/third_party/WebKit/Source/platform/fonts/opentype/FontFormatCheck.h
new file mode 100644
index 0000000..81e66dd1
--- /dev/null
+++ b/third_party/WebKit/Source/platform/fonts/opentype/FontFormatCheck.h
@@ -0,0 +1,33 @@
+// 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 FontFormatCheck_h
+#define FontFormatCheck_h
+
+#include "platform/wtf/Vector.h"
+#include "third_party/skia/include/core/SkData.h"
+#include "third_party/skia/include/core/SkRefCnt.h"
+#include "third_party/skia/include/core/SkTypeface.h"
+
+namespace blink {
+
+class FontFormatCheck {
+ public:
+  FontFormatCheck(sk_sp<SkData>);
+  bool IsVariableFont();
+  bool IsCbdtCblcColorFont();
+  bool IsSbixColorFont();
+  bool IsCff2OutlineFont();
+
+  // Still needed in FontCustomPlatformData.
+  static bool IsVariableFont(sk_sp<SkTypeface>);
+
+ private:
+  // hb-common.h: typedef uint32_t hb_tag_t;
+  Vector<uint32_t> table_tags_;
+};
+
+}  // namespace blink
+
+#endif
diff --git a/third_party/WebKit/Source/platform/fonts/opentype/VariableFontCheck.cpp b/third_party/WebKit/Source/platform/fonts/opentype/VariableFontCheck.cpp
deleted file mode 100644
index f948a6a..0000000
--- a/third_party/WebKit/Source/platform/fonts/opentype/VariableFontCheck.cpp
+++ /dev/null
@@ -1,21 +0,0 @@
-// Copyright 2017 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "platform/fonts/opentype/VariableFontCheck.h"
-
-#include "SkTypeface.h"
-#include "platform/wtf/Vector.h"
-
-namespace blink {
-
-bool VariableFontCheck::IsVariableFont(SkTypeface* typeface) {
-  const size_t table_count = typeface->countTables();
-  Vector<SkFontTableTag> table_tags;
-  table_tags.resize(table_count);
-  int table_tags_result = typeface->getTableTags(table_tags.data());
-  return table_tags_result && table_tags.Contains(SkFontTableTag(
-                                  SkSetFourByteTag('f', 'v', 'a', 'r')));
-}
-
-}  // namespace blink
diff --git a/third_party/WebKit/Source/platform/fonts/opentype/VariableFontCheck.h b/third_party/WebKit/Source/platform/fonts/opentype/VariableFontCheck.h
deleted file mode 100644
index e78fc54..0000000
--- a/third_party/WebKit/Source/platform/fonts/opentype/VariableFontCheck.h
+++ /dev/null
@@ -1,19 +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 VariableFontCheck_h
-#define VariableFontCheck_h
-
-class SkTypeface;
-
-namespace blink {
-
-class VariableFontCheck {
- public:
-  static bool IsVariableFont(SkTypeface*);
-};
-
-}  // namespace blink
-
-#endif
diff --git a/third_party/WebKit/Source/platform/fonts/shaping/HarfBuzzFace.cpp b/third_party/WebKit/Source/platform/fonts/shaping/HarfBuzzFace.cpp
index 1b2c654..b3650d4 100644
--- a/third_party/WebKit/Source/platform/fonts/shaping/HarfBuzzFace.cpp
+++ b/third_party/WebKit/Source/platform/fonts/shaping/HarfBuzzFace.cpp
@@ -284,6 +284,11 @@
               HarfBuzzFontData::SpaceGlyphInOpenTypeTables::Present);
 }
 
+unsigned HarfBuzzFace::UnitsPerEmFromHeadTable() {
+  hb_face_t* face = hb_font_get_face(unscaled_font_);
+  return hb_face_get_upem(face);
+}
+
 static hb_font_funcs_t* HarfBuzzSkiaGetFontFuncs() {
   hb_font_funcs_t* funcs = FontGlobalContext::GetHarfBuzzFontFuncs();
 
diff --git a/third_party/WebKit/Source/platform/fonts/shaping/HarfBuzzFace.h b/third_party/WebKit/Source/platform/fonts/shaping/HarfBuzzFace.h
index 2c70e4dd..b434cc6 100644
--- a/third_party/WebKit/Source/platform/fonts/shaping/HarfBuzzFace.h
+++ b/third_party/WebKit/Source/platform/fonts/shaping/HarfBuzzFace.h
@@ -66,6 +66,7 @@
                            VerticalLayoutCallbacks) const;
 
   bool HasSpaceInLigaturesOrKerning(TypesettingFeatures);
+  unsigned UnitsPerEmFromHeadTable();
 
  private:
   HarfBuzzFace(FontPlatformData*, uint64_t);
diff --git a/third_party/WebKit/Source/platform/fonts/shaping/HarfBuzzFontCache.h b/third_party/WebKit/Source/platform/fonts/shaping/HarfBuzzFontCache.h
index e8584cd..e6a6aaa 100644
--- a/third_party/WebKit/Source/platform/fonts/shaping/HarfBuzzFontCache.h
+++ b/third_party/WebKit/Source/platform/fonts/shaping/HarfBuzzFontCache.h
@@ -69,7 +69,12 @@
       // integer Height()
       height_fallback_ = lroundf(ascent) + lroundf(descent);
 
-      int units_per_em = paint.refTypeface()->getUnitsPerEm();
+      int units_per_em =
+          platform_data.GetHarfBuzzFace()->UnitsPerEmFromHeadTable();
+      if (!units_per_em) {
+        DLOG(ERROR)
+            << "Units per EM is 0 for font used in vertical writing mode.";
+      }
       size_per_unit_ = platform_data.size() / (units_per_em ? units_per_em : 1);
     } else {
       ascent_fallback_ = kInvalidFallbackMetricsValue;
diff --git a/third_party/WebKit/Source/platform/graphics/GraphicsLayer.cpp b/third_party/WebKit/Source/platform/graphics/GraphicsLayer.cpp
index 6752445f..c6bd2a4 100644
--- a/third_party/WebKit/Source/platform/graphics/GraphicsLayer.cpp
+++ b/third_party/WebKit/Source/platform/graphics/GraphicsLayer.cpp
@@ -119,6 +119,7 @@
 }
 
 GraphicsLayer::~GraphicsLayer() {
+  layer_->Layer()->SetLayerClient(nullptr);
   for (size_t i = 0; i < link_highlights_.size(); ++i)
     link_highlights_[i]->ClearCurrentGraphicsLayer();
   link_highlights_.clear();
diff --git a/third_party/WebKit/Source/platform/heap/BUILD.gn b/third_party/WebKit/Source/platform/heap/BUILD.gn
index 844c91f..32ef45c 100644
--- a/third_party/WebKit/Source/platform/heap/BUILD.gn
+++ b/third_party/WebKit/Source/platform/heap/BUILD.gn
@@ -46,6 +46,8 @@
     "HeapTerminatedArray.h",
     "HeapTerminatedArrayBuilder.h",
     "HeapTraits.h",
+    "MarkingVisitor.cpp",
+    "MarkingVisitor.h",
     "Member.h",
     "PageMemory.cpp",
     "PageMemory.h",
@@ -66,9 +68,7 @@
     "ThreadState.h",
     "ThreadingTraits.h",
     "TraceTraits.h",
-    "Visitor.cpp",
     "Visitor.h",
-    "VisitorImpl.h",
   ]
 
   deps = [
diff --git a/third_party/WebKit/Source/platform/heap/Handle.h b/third_party/WebKit/Source/platform/heap/Handle.h
index 9bcd2aba..2372ca1 100644
--- a/third_party/WebKit/Source/platform/heap/Handle.h
+++ b/third_party/WebKit/Source/platform/heap/Handle.h
@@ -39,7 +39,6 @@
 #include "platform/heap/ThreadState.h"
 #include "platform/heap/TraceTraits.h"
 #include "platform/heap/Visitor.h"
-#include "platform/heap/VisitorImpl.h"
 #include "platform/wtf/Allocator.h"
 #include "platform/wtf/Noncopyable.h"
 
diff --git a/third_party/WebKit/Source/platform/heap/Heap.h b/third_party/WebKit/Source/platform/heap/Heap.h
index 18ef6a9..e8d58bd 100644
--- a/third_party/WebKit/Source/platform/heap/Heap.h
+++ b/third_party/WebKit/Source/platform/heap/Heap.h
@@ -752,6 +752,4 @@
 
 }  // namespace blink
 
-#include "platform/heap/VisitorImpl.h"
-
 #endif  // Heap_h
diff --git a/third_party/WebKit/Source/platform/heap/HeapAllocator.h b/third_party/WebKit/Source/platform/heap/HeapAllocator.h
index ec6c9ad..840c1d9e 100644
--- a/third_party/WebKit/Source/platform/heap/HeapAllocator.h
+++ b/third_party/WebKit/Source/platform/heap/HeapAllocator.h
@@ -189,12 +189,12 @@
   }
 
   template <typename VisitorDispatcher>
-  static void RegisterWeakTable(VisitorDispatcher visitor,
+  static bool RegisterWeakTable(VisitorDispatcher visitor,
                                 const void* closure,
                                 EphemeronCallback iteration_callback,
                                 EphemeronCallback iteration_done_callback) {
-    visitor->RegisterWeakTable(closure, iteration_callback,
-                               iteration_done_callback);
+    return visitor->RegisterWeakTable(closure, iteration_callback,
+                                      iteration_done_callback);
   }
 
 #if DCHECK_IS_ON()
@@ -243,9 +243,9 @@
       // children but rather push all blink::GarbageCollected objects and only
       // eagerly trace non-managed objects.
       DCHECK(!thread_state->Heap().GetStackFrameDepth().IsEnabled());
+      // No weak handling for write barriers. Modifying weakly reachable objects
+      // strongifies them for the current cycle.
       TraceCollectionIfEnabled<
-          // No weak handling for write barriers. The weak references will be
-          // updated in the atomic pause.
           WTF::kNoWeakHandling, T, Traits>::Trace(thread_state
                                                       ->CurrentVisitor(),
                                                   *object);
@@ -264,10 +264,10 @@
       ThreadState::NoAllocationScope no_allocation_scope(thread_state);
       DCHECK(thread_state->CurrentVisitor());
       DCHECK(!thread_state->Heap().GetStackFrameDepth().IsEnabled());
+      // No weak handling for write barriers. Modifying weakly reachable objects
+      // strongifies them for the current cycle.
       while (len-- > 0) {
         TraceCollectionIfEnabled<
-            // No weak handling for write barriers. The weak references will be
-            // updated in the atomic pause.
             WTF::kNoWeakHandling, T, Traits>::Trace(thread_state
                                                         ->CurrentVisitor(),
                                                     *array);
diff --git a/third_party/WebKit/Source/platform/heap/IncrementalMarkingTest.cpp b/third_party/WebKit/Source/platform/heap/IncrementalMarkingTest.cpp
index 1b21319..9cef009f 100644
--- a/third_party/WebKit/Source/platform/heap/IncrementalMarkingTest.cpp
+++ b/third_party/WebKit/Source/platform/heap/IncrementalMarkingTest.cpp
@@ -817,22 +817,26 @@
 
 TEST(IncrementalMarkingTest, HeapHashSetInsert) {
   Insert<HeapHashSet<Member<Object>>>();
-  InsertNoBarrier<HeapHashSet<WeakMember<Object>>>();
+  // Weak references are strongified for the current cycle.
+  Insert<HeapHashSet<WeakMember<Object>>>();
 }
 
 TEST(IncrementalMarkingTest, HeapHashSetCopy) {
   Copy<HeapHashSet<Member<Object>>>();
-  CopyNoBarrier<HeapHashSet<WeakMember<Object>>>();
+  // Weak references are strongified for the current cycle.
+  Copy<HeapHashSet<WeakMember<Object>>>();
 }
 
 TEST(IncrementalMarkingTest, HeapHashSetMove) {
   Move<HeapHashSet<Member<Object>>>();
-  MoveNoBarrier<HeapHashSet<WeakMember<Object>>>();
+  // Weak references are strongified for the current cycle.
+  Move<HeapHashSet<WeakMember<Object>>>();
 }
 
 TEST(IncrementalMarkingTest, HeapHashSetSwap) {
   Swap<HeapHashSet<Member<Object>>>();
-  SwapNoBarrier<HeapHashSet<WeakMember<Object>>>();
+  // Weak references are strongified for the current cycle.
+  Swap<HeapHashSet<WeakMember<Object>>>();
 }
 
 class StrongWeakPair : public std::pair<Member<Object>, WeakMember<Object>> {
@@ -959,22 +963,26 @@
 
 TEST(IncrementalMarkingTest, HeapLinkedHashSetInsert) {
   Insert<HeapLinkedHashSet<Member<Object>>>();
-  InsertNoBarrier<HeapLinkedHashSet<WeakMember<Object>>>();
+  // Weak references are strongified for the current cycle.
+  Insert<HeapLinkedHashSet<WeakMember<Object>>>();
 }
 
 TEST(IncrementalMarkingTest, HeapLinkedHashSetCopy) {
   Copy<HeapLinkedHashSet<Member<Object>>>();
-  CopyNoBarrier<HeapLinkedHashSet<WeakMember<Object>>>();
+  // Weak references are strongified for the current cycle.
+  Copy<HeapLinkedHashSet<WeakMember<Object>>>();
 }
 
 TEST(IncrementalMarkingTest, HeapLinkedHashSetMove) {
   Move<HeapLinkedHashSet<Member<Object>>>();
-  MoveNoBarrier<HeapLinkedHashSet<WeakMember<Object>>>();
+  // Weak references are strongified for the current cycle.
+  Move<HeapLinkedHashSet<WeakMember<Object>>>();
 }
 
 TEST(IncrementalMarkingTest, HeapLinkedHashSetSwap) {
   Swap<HeapLinkedHashSet<Member<Object>>>();
-  SwapNoBarrier<HeapLinkedHashSet<WeakMember<Object>>>();
+  // Weak references are strongified for the current cycle.
+  Move<HeapLinkedHashSet<WeakMember<Object>>>();
 }
 
 // =============================================================================
@@ -985,7 +993,8 @@
 
 TEST(IncrementalMarkingTest, HeapHashCountedSetInsert) {
   Insert<HeapHashCountedSet<Member<Object>>>();
-  InsertNoBarrier<HeapHashCountedSet<WeakMember<Object>>>();
+  // Weak references are strongified for the current cycle.
+  Insert<HeapHashCountedSet<WeakMember<Object>>>();
 }
 
 TEST(IncrementalMarkingTest, HeapHashCountedSetSwap) {
@@ -1011,8 +1020,9 @@
     HeapHashCountedSet<WeakMember<Object>> container2;
     container2.insert(obj2);
     {
-      ExpectNoWriteBarrierFires<Object> scope(ThreadState::Current(),
-                                              {obj1, obj2});
+      // Weak references are strongified for the current cycle.
+      ExpectWriteBarrierFires<Object> scope(ThreadState::Current(),
+                                            {obj1, obj2});
       container1.swap(container2);
     }
   }
@@ -1083,8 +1093,8 @@
   Object* obj2 = Object::Create();
   HeapHashMap<WeakMember<Object>, WeakMember<Object>> map;
   {
-    ExpectNoWriteBarrierFires<Object> scope(ThreadState::Current(),
-                                            {obj1, obj2});
+    // Weak references are strongified for the current cycle.
+    ExpectWriteBarrierFires<Object> scope(ThreadState::Current(), {obj1, obj2});
     map.insert(obj1, obj2);
   }
 }
@@ -1094,7 +1104,8 @@
   Object* obj2 = Object::Create();
   HeapHashMap<Member<Object>, WeakMember<Object>> map;
   {
-    ExpectWriteBarrierFires<Object> scope(ThreadState::Current(), {obj1});
+    // Weak references are strongified for the current cycle.
+    ExpectWriteBarrierFires<Object> scope(ThreadState::Current(), {obj1, obj2});
     map.insert(obj1, obj2);
   }
 }
@@ -1104,7 +1115,8 @@
   Object* obj2 = Object::Create();
   HeapHashMap<WeakMember<Object>, Member<Object>> map;
   {
-    ExpectWriteBarrierFires<Object> scope(ThreadState::Current(), {obj2});
+    // Weak references are strongified for the current cycle.
+    ExpectWriteBarrierFires<Object> scope(ThreadState::Current(), {obj1, obj2});
     map.insert(obj1, obj2);
   }
 }
@@ -1183,8 +1195,8 @@
   HeapHashMap<WeakMember<Object>, WeakMember<Object>> map1;
   map1.insert(obj1, obj2);
   {
-    ExpectNoWriteBarrierFires<Object> scope(ThreadState::Current(),
-                                            {obj1, obj2});
+    // Weak references are strongified for the current cycle.
+    ExpectWriteBarrierFires<Object> scope(ThreadState::Current(), {obj1, obj2});
     EXPECT_TRUE(map1.Contains(obj1));
     HeapHashMap<WeakMember<Object>, WeakMember<Object>> map2(map1);
     EXPECT_TRUE(map1.Contains(obj1));
@@ -1198,7 +1210,8 @@
   HeapHashMap<Member<Object>, WeakMember<Object>> map1;
   map1.insert(obj1, obj2);
   {
-    ExpectWriteBarrierFires<Object> scope(ThreadState::Current(), {obj1});
+    // Weak references are strongified for the current cycle.
+    ExpectWriteBarrierFires<Object> scope(ThreadState::Current(), {obj1, obj2});
     EXPECT_TRUE(map1.Contains(obj1));
     HeapHashMap<Member<Object>, WeakMember<Object>> map2(map1);
     EXPECT_TRUE(map1.Contains(obj1));
@@ -1212,7 +1225,8 @@
   HeapHashMap<WeakMember<Object>, Member<Object>> map1;
   map1.insert(obj1, obj2);
   {
-    ExpectWriteBarrierFires<Object> scope(ThreadState::Current(), {obj2});
+    // Weak references are strongified for the current cycle.
+    ExpectWriteBarrierFires<Object> scope(ThreadState::Current(), {obj1, obj2});
     EXPECT_TRUE(map1.Contains(obj1));
     HeapHashMap<WeakMember<Object>, Member<Object>> map2(map1);
     EXPECT_TRUE(map1.Contains(obj1));
@@ -1237,8 +1251,8 @@
   HeapHashMap<WeakMember<Object>, WeakMember<Object>> map1;
   map1.insert(obj1, obj2);
   {
-    ExpectNoWriteBarrierFires<Object> scope(ThreadState::Current(),
-                                            {obj1, obj2});
+    // Weak references are strongified for the current cycle.
+    ExpectWriteBarrierFires<Object> scope(ThreadState::Current(), {obj1, obj2});
     HeapHashMap<WeakMember<Object>, WeakMember<Object>> map2(std::move(map1));
   }
 }
@@ -1249,7 +1263,8 @@
   HeapHashMap<Member<Object>, WeakMember<Object>> map1;
   map1.insert(obj1, obj2);
   {
-    ExpectWriteBarrierFires<Object> scope(ThreadState::Current(), {obj1});
+    // Weak references are strongified for the current cycle.
+    ExpectWriteBarrierFires<Object> scope(ThreadState::Current(), {obj1, obj2});
     HeapHashMap<Member<Object>, WeakMember<Object>> map2(std::move(map1));
   }
 }
@@ -1260,7 +1275,8 @@
   HeapHashMap<WeakMember<Object>, Member<Object>> map1;
   map1.insert(obj1, obj2);
   {
-    ExpectWriteBarrierFires<Object> scope(ThreadState::Current(), {obj2});
+    // Weak references are strongified for the current cycle.
+    ExpectWriteBarrierFires<Object> scope(ThreadState::Current(), {obj1, obj2});
     HeapHashMap<WeakMember<Object>, Member<Object>> map2(std::move(map1));
   }
 }
@@ -1291,8 +1307,9 @@
   HeapHashMap<WeakMember<Object>, WeakMember<Object>> map2;
   map2.insert(obj3, obj4);
   {
-    ExpectNoWriteBarrierFires<Object> scope(ThreadState::Current(),
-                                            {obj1, obj2, obj3, obj4});
+    // Weak references are strongified for the current cycle.
+    ExpectWriteBarrierFires<Object> scope(ThreadState::Current(),
+                                          {obj1, obj2, obj3, obj4});
     std::swap(map1, map2);
   }
 }
@@ -1307,7 +1324,9 @@
   HeapHashMap<Member<Object>, WeakMember<Object>> map2;
   map2.insert(obj3, obj4);
   {
-    ExpectWriteBarrierFires<Object> scope(ThreadState::Current(), {obj1, obj3});
+    // Weak references are strongified for the current cycle.
+    ExpectWriteBarrierFires<Object> scope(ThreadState::Current(),
+                                          {obj1, obj2, obj3, obj4});
     std::swap(map1, map2);
   }
 }
@@ -1322,7 +1341,9 @@
   HeapHashMap<WeakMember<Object>, Member<Object>> map2;
   map2.insert(obj3, obj4);
   {
-    ExpectWriteBarrierFires<Object> scope(ThreadState::Current(), {obj2, obj4});
+    // Weak references are strongified for the current cycle.
+    ExpectWriteBarrierFires<Object> scope(ThreadState::Current(),
+                                          {obj1, obj2, obj3, obj4});
     std::swap(map1, map2);
   }
 }
diff --git a/third_party/WebKit/Source/platform/heap/MarkingVisitor.cpp b/third_party/WebKit/Source/platform/heap/MarkingVisitor.cpp
new file mode 100644
index 0000000..4ed55c4
--- /dev/null
+++ b/third_party/WebKit/Source/platform/heap/MarkingVisitor.cpp
@@ -0,0 +1,76 @@
+// 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 "platform/heap/MarkingVisitor.h"
+
+#include "platform/heap/Heap.h"
+#include "platform/heap/ThreadState.h"
+
+namespace blink {
+
+std::unique_ptr<MarkingVisitor> MarkingVisitor::Create(ThreadState* state,
+                                                       MarkingMode mode) {
+  return std::make_unique<MarkingVisitor>(state, mode);
+}
+
+MarkingVisitor::MarkingVisitor(ThreadState* state, MarkingMode marking_mode)
+    : Visitor(state), marking_mode_(marking_mode) {
+  // See ThreadState::runScheduledGC() why we need to already be in a
+  // GCForbiddenScope before any safe point is entered.
+  DCHECK(state->IsGCForbidden());
+#if DCHECK_IS_ON()
+  DCHECK(state->CheckThread());
+#endif
+}
+
+MarkingVisitor::~MarkingVisitor() = default;
+
+void MarkingVisitor::MarkNoTracingCallback(Visitor* visitor, void* object) {
+  // TODO(mlippautz): Remove cast;
+  reinterpret_cast<MarkingVisitor*>(visitor)->MarkNoTracing(object);
+}
+
+void MarkingVisitor::RegisterWeakCallback(void* closure,
+                                          WeakCallback callback) {
+  DCHECK(GetMarkingMode() != kWeakProcessing);
+  // We don't want to run weak processings when taking a snapshot.
+  if (GetMarkingMode() == kSnapshotMarking)
+    return;
+  Heap().PushWeakCallback(closure, callback);
+}
+
+void MarkingVisitor::RegisterBackingStoreReference(void* slot) {
+  if (GetMarkingMode() != kGlobalMarkingWithCompaction)
+    return;
+  Heap().RegisterMovingObjectReference(
+      reinterpret_cast<MovableReference*>(slot));
+}
+
+void MarkingVisitor::RegisterBackingStoreCallback(void* backing_store,
+                                                  MovingObjectCallback callback,
+                                                  void* callback_data) {
+  if (GetMarkingMode() != kGlobalMarkingWithCompaction)
+    return;
+  Heap().RegisterMovingObjectCallback(
+      reinterpret_cast<MovableReference>(backing_store), callback,
+      callback_data);
+}
+
+bool MarkingVisitor::RegisterWeakTable(
+    const void* closure,
+    EphemeronCallback iteration_callback,
+    EphemeronCallback iteration_done_callback) {
+  DCHECK(GetMarkingMode() != kWeakProcessing);
+  Heap().RegisterWeakTable(const_cast<void*>(closure), iteration_callback,
+                           iteration_done_callback);
+  return true;
+}
+
+#if DCHECK_IS_ON()
+bool MarkingVisitor::WeakTableRegistered(const void* closure) {
+  return Heap().WeakTableRegistered(closure);
+}
+#endif
+
+}  // namespace blink
diff --git a/third_party/WebKit/Source/platform/heap/MarkingVisitor.h b/third_party/WebKit/Source/platform/heap/MarkingVisitor.h
new file mode 100644
index 0000000..ef50300
--- /dev/null
+++ b/third_party/WebKit/Source/platform/heap/MarkingVisitor.h
@@ -0,0 +1,151 @@
+// 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 MarkingVisitor_h
+#define MarkingVisitor_h
+
+#include "platform/heap/Heap.h"
+#include "platform/heap/HeapPage.h"
+#include "platform/heap/Visitor.h"
+
+namespace blink {
+
+// Visitor used to mark Oilpan objects.
+class PLATFORM_EXPORT MarkingVisitor final : public Visitor {
+ public:
+  enum MarkingMode {
+    // This is a default visitor. This is used for GCType=GCWithSweep
+    // and GCType=GCWithoutSweep.
+    kGlobalMarking,
+    // This visitor just marks objects and ignores weak processing.
+    // This is used for GCType=TakeSnapshot.
+    kSnapshotMarking,
+    // This visitor is used to trace objects during weak processing.
+    // This visitor is allowed to trace only already marked objects.
+    kWeakProcessing,
+    // Perform global marking along with preparing for additional sweep
+    // compaction of heap arenas afterwards. Compared to the GlobalMarking
+    // visitor, this visitor will also register references to objects
+    // that might be moved during arena compaction -- the compaction
+    // pass will then fix up those references when the object move goes
+    // ahead.
+    kGlobalMarkingWithCompaction,
+  };
+
+  static std::unique_ptr<MarkingVisitor> Create(ThreadState*, MarkingMode);
+
+  MarkingVisitor(ThreadState*, MarkingMode);
+  virtual ~MarkingVisitor();
+
+  inline MarkingMode GetMarkingMode() const { return marking_mode_; }
+
+  // Marking implementation.
+
+  // This method marks an object and adds it to the set of objects that should
+  // have their trace method called. Since not all objects have vtables we have
+  // to have the callback as an explicit argument, but we can use the templated
+  // one-argument mark method above to automatically provide the callback
+  // function.
+  inline void Mark(const void* object_pointer, TraceCallback);
+
+  // Used to mark objects during conservative scanning.
+  inline void MarkHeader(HeapObjectHeader*, TraceCallback);
+  inline void MarkHeaderNoTracing(HeapObjectHeader*);
+
+  // Marks the header of an object. Is used for eagerly tracing of objects.
+  inline bool EnsureMarked(const void* pointer);
+
+  // Used for eagerly marking objects and for delayed marking of backing stores
+  // when the actual payload is processed differently, e.g., by weak handling.
+  inline void MarkNoTracing(const void* pointer) {
+    Mark(pointer, reinterpret_cast<TraceCallback>(0));
+  }
+
+  // Implementation of the visitor interface. See above for descriptions.
+
+  void Visit(void* object,
+             TraceCallback trace_callback,
+             TraceCallback mark_callback) final {
+    mark_callback(this, object);
+  }
+
+  void RegisterBackingStoreReference(void* slot) final;
+  void RegisterBackingStoreCallback(void* backing_store,
+                                    MovingObjectCallback,
+                                    void* callback_data) final;
+  void RegisterDelayedMarkNoTracing(const void* pointer) final;
+  bool RegisterWeakTable(const void* closure,
+                         EphemeronCallback iteration_callback,
+                         EphemeronCallback iteration_done_callback) final;
+#if DCHECK_IS_ON()
+  bool WeakTableRegistered(const void* closure) final;
+#endif
+  void RegisterWeakCallback(void* closure, WeakCallback) final;
+
+ private:
+  static void MarkNoTracingCallback(Visitor*, void*);
+
+  const MarkingMode marking_mode_;
+};
+
+inline void MarkingVisitor::MarkHeader(HeapObjectHeader* header,
+                                       TraceCallback callback) {
+  DCHECK(header);
+  if (header->IsMarked())
+    return;
+
+  DCHECK(ThreadState::Current()->IsInGC() ||
+         ThreadState::Current()->IsIncrementalMarking());
+  DCHECK(GetMarkingMode() != kWeakProcessing);
+
+  const void* object_pointer = header->Payload();
+  // A GC should only mark the objects that belong in its heap.
+  DCHECK(&PageFromObject(object_pointer)->Arena()->GetThreadState()->Heap() ==
+         &Heap());
+
+  header->Mark();
+
+  if (callback)
+    Heap().PushTraceCallback(const_cast<void*>(object_pointer), callback);
+}
+
+inline void MarkingVisitor::Mark(const void* object_pointer,
+                                 TraceCallback callback) {
+  if (!object_pointer)
+    return;
+  HeapObjectHeader* header = HeapObjectHeader::FromPayload(object_pointer);
+  MarkHeader(header, callback);
+}
+
+inline void MarkingVisitor::MarkHeaderNoTracing(HeapObjectHeader* header) {
+  MarkHeader(header, reinterpret_cast<TraceCallback>(0));
+}
+
+inline void MarkingVisitor::RegisterDelayedMarkNoTracing(
+    const void* object_pointer) {
+  DCHECK(GetMarkingMode() != kWeakProcessing);
+  Heap().PushPostMarkingCallback(const_cast<void*>(object_pointer),
+                                 &MarkNoTracingCallback);
+}
+
+inline bool MarkingVisitor::EnsureMarked(const void* object_pointer) {
+  if (!object_pointer)
+    return false;
+
+  HeapObjectHeader* header = HeapObjectHeader::FromPayload(object_pointer);
+  if (header->IsMarked())
+    return false;
+#if DCHECK_IS_ON()
+  MarkNoTracing(object_pointer);
+#else
+  // Inline what the above markNoTracing() call expands to,
+  // so as to make sure that we do get all the benefits (asserts excepted.)
+  header->Mark();
+#endif
+  return true;
+}
+
+}  // namespace blink
+
+#endif  // MarkingVisitor_h
diff --git a/third_party/WebKit/Source/platform/heap/ObjectStartBitmapTest.cpp b/third_party/WebKit/Source/platform/heap/ObjectStartBitmapTest.cpp
index f2e2112..88504c0 100644
--- a/third_party/WebKit/Source/platform/heap/ObjectStartBitmapTest.cpp
+++ b/third_party/WebKit/Source/platform/heap/ObjectStartBitmapTest.cpp
@@ -4,7 +4,6 @@
 
 #include "platform/heap/HeapPage.h"
 
-#include "platform/heap/VisitorImpl.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
 namespace blink {
diff --git a/third_party/WebKit/Source/platform/heap/ProcessHeap.cpp b/third_party/WebKit/Source/platform/heap/ProcessHeap.cpp
index dd467ce..0f0143d 100644
--- a/third_party/WebKit/Source/platform/heap/ProcessHeap.cpp
+++ b/third_party/WebKit/Source/platform/heap/ProcessHeap.cpp
@@ -7,7 +7,6 @@
 #include "platform/heap/CallbackStack.h"
 #include "platform/heap/GCInfo.h"
 #include "platform/heap/PersistentNode.h"
-#include "platform/heap/VisitorImpl.h"
 #include "platform/wtf/Assertions.h"
 
 namespace blink {
diff --git a/third_party/WebKit/Source/platform/heap/TraceTraits.h b/third_party/WebKit/Source/platform/heap/TraceTraits.h
index 7ba6ec9..3090c97 100644
--- a/third_party/WebKit/Source/platform/heap/TraceTraits.h
+++ b/third_party/WebKit/Source/platform/heap/TraceTraits.h
@@ -8,6 +8,7 @@
 #include "platform/bindings/ScriptWrappableVisitor.h"
 #include "platform/heap/GCInfo.h"
 #include "platform/heap/Heap.h"
+#include "platform/heap/MarkingVisitor.h"
 #include "platform/heap/StackFrameDepth.h"
 #include "platform/heap/Visitor.h"
 #include "platform/wtf/Allocator.h"
diff --git a/third_party/WebKit/Source/platform/heap/Visitor.cpp b/third_party/WebKit/Source/platform/heap/Visitor.cpp
deleted file mode 100644
index 20ddf6c..0000000
--- a/third_party/WebKit/Source/platform/heap/Visitor.cpp
+++ /dev/null
@@ -1,41 +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.
-
-#include "platform/heap/Visitor.h"
-
-#include <memory>
-#include "platform/heap/BlinkGC.h"
-#include "platform/heap/ThreadState.h"
-#include "platform/heap/VisitorImpl.h"
-#include "platform/wtf/PtrUtil.h"
-
-namespace blink {
-
-Visitor::Visitor(ThreadState* state) : state_(state) {}
-
-Visitor::~Visitor() = default;
-
-std::unique_ptr<MarkingVisitor> MarkingVisitor::Create(ThreadState* state,
-                                                       MarkingMode mode) {
-  return std::make_unique<MarkingVisitor>(state, mode);
-}
-
-MarkingVisitor::MarkingVisitor(ThreadState* state, MarkingMode marking_mode)
-    : Visitor(state), marking_mode_(marking_mode) {
-  // See ThreadState::runScheduledGC() why we need to already be in a
-  // GCForbiddenScope before any safe point is entered.
-  DCHECK(state->IsGCForbidden());
-#if DCHECK_IS_ON()
-  DCHECK(state->CheckThread());
-#endif
-}
-
-MarkingVisitor::~MarkingVisitor() = default;
-
-void MarkingVisitor::MarkNoTracingCallback(Visitor* visitor, void* object) {
-  // TODO(mlippautz): Remove cast;
-  reinterpret_cast<MarkingVisitor*>(visitor)->MarkNoTracing(object);
-}
-
-}  // namespace blink
diff --git a/third_party/WebKit/Source/platform/heap/Visitor.h b/third_party/WebKit/Source/platform/heap/Visitor.h
index 3c409d8..ceebdb3c 100644
--- a/third_party/WebKit/Source/platform/heap/Visitor.h
+++ b/third_party/WebKit/Source/platform/heap/Visitor.h
@@ -44,7 +44,6 @@
 
 template <typename T>
 class GarbageCollected;
-class HeapObjectHeader;
 template <typename T>
 class TraceTrait;
 class ThreadState;
@@ -71,8 +70,8 @@
 // Visitor is used to traverse Oilpan's object graph.
 class PLATFORM_EXPORT Visitor {
  public:
-  Visitor(ThreadState*);
-  virtual ~Visitor();
+  explicit Visitor(ThreadState* state) : state_(state) {}
+  virtual ~Visitor() = default;
 
   inline ThreadState* GetState() const { return state_; }
   inline ThreadHeap& Heap() const { return GetState()->Heap(); }
@@ -190,11 +189,14 @@
   virtual void RegisterDelayedMarkNoTracing(const void* pointer) = 0;
 
   // Used to register ephemeron callbacks.
-  virtual void RegisterWeakTable(const void* closure,
+  virtual bool RegisterWeakTable(const void* closure,
                                  EphemeronCallback iteration_callback,
-                                 EphemeronCallback iteration_done_callback) = 0;
+                                 EphemeronCallback iteration_done_callback) {
+    return false;
+  }
+
 #if DCHECK_IS_ON()
-  virtual bool WeakTableRegistered(const void* closure) = 0;
+  virtual bool WeakTableRegistered(const void* closure) { return false; }
 #endif
 
   // |WeakCallback| will usually use |ObjectAliveTrait| to figure out liveness
@@ -214,84 +216,6 @@
   ThreadState* const state_;
 };
 
-// Visitor used to mark Oilpan objects.
-class PLATFORM_EXPORT MarkingVisitor final : public Visitor {
- public:
-  enum MarkingMode {
-    // This is a default visitor. This is used for GCType=GCWithSweep
-    // and GCType=GCWithoutSweep.
-    kGlobalMarking,
-    // This visitor just marks objects and ignores weak processing.
-    // This is used for GCType=TakeSnapshot.
-    kSnapshotMarking,
-    // This visitor is used to trace objects during weak processing.
-    // This visitor is allowed to trace only already marked objects.
-    kWeakProcessing,
-    // Perform global marking along with preparing for additional sweep
-    // compaction of heap arenas afterwards. Compared to the GlobalMarking
-    // visitor, this visitor will also register references to objects
-    // that might be moved during arena compaction -- the compaction
-    // pass will then fix up those references when the object move goes
-    // ahead.
-    kGlobalMarkingWithCompaction,
-  };
-
-  static std::unique_ptr<MarkingVisitor> Create(ThreadState*, MarkingMode);
-
-  MarkingVisitor(ThreadState*, MarkingMode);
-  virtual ~MarkingVisitor();
-
-  inline MarkingMode GetMarkingMode() const { return marking_mode_; }
-
-  // Marking implementation.
-
-  // This method marks an object and adds it to the set of objects that should
-  // have their trace method called. Since not all objects have vtables we have
-  // to have the callback as an explicit argument, but we can use the templated
-  // one-argument mark method above to automatically provide the callback
-  // function.
-  inline void Mark(const void* object_pointer, TraceCallback);
-
-  // Used to mark objects during conservative scanning.
-  inline void MarkHeader(HeapObjectHeader*, TraceCallback);
-  inline void MarkHeaderNoTracing(HeapObjectHeader*);
-
-  // Marks the header of an object. Is used for eagerly tracing of objects.
-  inline bool EnsureMarked(const void* pointer);
-
-  // Used for eagerly marking objects and for delayed marking of backing stores
-  // when the actual payload is processed differently, e.g., by weak handling.
-  inline void MarkNoTracing(const void* pointer) {
-    Mark(pointer, reinterpret_cast<TraceCallback>(0));
-  }
-
-  // Implementation of the visitor interface. See above for descriptions.
-
-  void Visit(void* object,
-             TraceCallback trace_callback,
-             TraceCallback mark_callback) final {
-    mark_callback(this, object);
-  }
-
-  void RegisterBackingStoreReference(void* slot) final;
-  void RegisterBackingStoreCallback(void* backing_store,
-                                    MovingObjectCallback,
-                                    void* callback_data) final;
-  void RegisterDelayedMarkNoTracing(const void* pointer) final;
-  void RegisterWeakTable(const void* closure,
-                         EphemeronCallback iteration_callback,
-                         EphemeronCallback iteration_done_callback) final;
-#if DCHECK_IS_ON()
-  bool WeakTableRegistered(const void* closure) final;
-#endif
-  void RegisterWeakCallback(void* closure, WeakCallback) final;
-
- private:
-  static void MarkNoTracingCallback(Visitor*, void*);
-
-  const MarkingMode marking_mode_;
-};
-
 }  // namespace blink
 
 #endif  // Visitor_h
diff --git a/third_party/WebKit/Source/platform/heap/VisitorImpl.h b/third_party/WebKit/Source/platform/heap/VisitorImpl.h
deleted file mode 100644
index 1a3f796..0000000
--- a/third_party/WebKit/Source/platform/heap/VisitorImpl.h
+++ /dev/null
@@ -1,116 +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 VisitorImpl_h
-#define VisitorImpl_h
-
-#include "platform/heap/Heap.h"
-#include "platform/heap/ThreadState.h"
-#include "platform/heap/Visitor.h"
-#include "platform/wtf/Allocator.h"
-
-namespace blink {
-
-inline void MarkingVisitor::MarkHeader(HeapObjectHeader* header,
-                                       TraceCallback callback) {
-  DCHECK(header);
-  if (header->IsMarked())
-    return;
-
-  DCHECK(ThreadState::Current()->IsInGC() ||
-         ThreadState::Current()->IsIncrementalMarking());
-  DCHECK(GetMarkingMode() != kWeakProcessing);
-
-  const void* object_pointer = header->Payload();
-  // A GC should only mark the objects that belong in its heap.
-  DCHECK(&PageFromObject(object_pointer)->Arena()->GetThreadState()->Heap() ==
-         &Heap());
-
-  header->Mark();
-
-  if (callback)
-    Heap().PushTraceCallback(const_cast<void*>(object_pointer), callback);
-}
-
-inline void MarkingVisitor::Mark(const void* object_pointer,
-                                 TraceCallback callback) {
-  if (!object_pointer)
-    return;
-  HeapObjectHeader* header = HeapObjectHeader::FromPayload(object_pointer);
-  MarkHeader(header, callback);
-}
-
-inline void MarkingVisitor::MarkHeaderNoTracing(HeapObjectHeader* header) {
-  MarkHeader(header, reinterpret_cast<TraceCallback>(0));
-}
-
-inline void MarkingVisitor::RegisterDelayedMarkNoTracing(
-    const void* object_pointer) {
-  DCHECK(GetMarkingMode() != kWeakProcessing);
-  Heap().PushPostMarkingCallback(const_cast<void*>(object_pointer),
-                                 &MarkNoTracingCallback);
-}
-
-inline void MarkingVisitor::RegisterWeakTable(
-    const void* closure,
-    EphemeronCallback iteration_callback,
-    EphemeronCallback iteration_done_callback) {
-  DCHECK(GetMarkingMode() != kWeakProcessing);
-  Heap().RegisterWeakTable(const_cast<void*>(closure), iteration_callback,
-                           iteration_done_callback);
-}
-
-#if DCHECK_IS_ON()
-inline bool MarkingVisitor::WeakTableRegistered(const void* closure) {
-  return Heap().WeakTableRegistered(closure);
-}
-#endif
-
-inline bool MarkingVisitor::EnsureMarked(const void* object_pointer) {
-  if (!object_pointer)
-    return false;
-
-  HeapObjectHeader* header = HeapObjectHeader::FromPayload(object_pointer);
-  if (header->IsMarked())
-    return false;
-#if DCHECK_IS_ON()
-  MarkNoTracing(object_pointer);
-#else
-  // Inline what the above markNoTracing() call expands to,
-  // so as to make sure that we do get all the benefits (asserts excepted.)
-  header->Mark();
-#endif
-  return true;
-}
-
-inline void MarkingVisitor::RegisterWeakCallback(void* closure,
-                                                 WeakCallback callback) {
-  DCHECK(GetMarkingMode() != kWeakProcessing);
-  // We don't want to run weak processings when taking a snapshot.
-  if (GetMarkingMode() == kSnapshotMarking)
-    return;
-  Heap().PushWeakCallback(closure, callback);
-}
-
-inline void MarkingVisitor::RegisterBackingStoreReference(void* slot) {
-  if (GetMarkingMode() != kGlobalMarkingWithCompaction)
-    return;
-  Heap().RegisterMovingObjectReference(
-      reinterpret_cast<MovableReference*>(slot));
-}
-
-inline void MarkingVisitor::RegisterBackingStoreCallback(
-    void* backing_store,
-    MovingObjectCallback callback,
-    void* callback_data) {
-  if (GetMarkingMode() != kGlobalMarkingWithCompaction)
-    return;
-  Heap().RegisterMovingObjectCallback(
-      reinterpret_cast<MovableReference>(backing_store), callback,
-      callback_data);
-}
-
-}  // namespace blink
-
-#endif
diff --git a/third_party/WebKit/Source/platform/image-decoders/ImageDecoder.cpp b/third_party/WebKit/Source/platform/image-decoders/ImageDecoder.cpp
index 5f7f27c..78d9be90 100644
--- a/third_party/WebKit/Source/platform/image-decoders/ImageDecoder.cpp
+++ b/third_party/WebKit/Source/platform/image-decoders/ImageDecoder.cpp
@@ -34,6 +34,8 @@
 
 namespace blink {
 
+const size_t ImageDecoder::kNoDecodedImageByteLimit;
+
 inline bool MatchesJPEGSignature(const char* contents) {
   return !memcmp(contents, "\xFF\xD8\xFF", 3);
 }
diff --git a/third_party/WebKit/Source/platform/image-decoders/png/PNGImageDecoderTest.cpp b/third_party/WebKit/Source/platform/image-decoders/png/PNGImageDecoderTest.cpp
index 26711ec..6e14bcf 100644
--- a/third_party/WebKit/Source/platform/image-decoders/png/PNGImageDecoderTest.cpp
+++ b/third_party/WebKit/Source/platform/image-decoders/png/PNGImageDecoderTest.cpp
@@ -34,9 +34,9 @@
 
 std::unique_ptr<ImageDecoder> CreatePNGDecoder(
     ImageDecoder::AlphaOption alpha_option) {
-  return WTF::WrapUnique(
-      new PNGImageDecoder(alpha_option, ColorBehavior::TransformToSRGB(),
-                          ImageDecoder::kNoDecodedImageByteLimit));
+  return std::make_unique<PNGImageDecoder>(
+      alpha_option, ColorBehavior::TransformToSRGB(),
+      ImageDecoder::kNoDecodedImageByteLimit);
 }
 
 std::unique_ptr<ImageDecoder> CreatePNGDecoder() {
@@ -46,7 +46,7 @@
 std::unique_ptr<ImageDecoder> CreatePNGDecoderWithPngData(
     const char* png_file) {
   auto decoder = CreatePNGDecoder();
-  auto data = ReadFile(png_file);
+  scoped_refptr<SharedBuffer> data = ReadFile(png_file);
   EXPECT_FALSE(data->IsEmpty());
   decoder->SetData(data.get(), true);
   return decoder;
@@ -64,7 +64,7 @@
                         size_t bytes_needed_to_decode_size,
                         IntSize expected_size) {
   auto decoder = CreatePNGDecoder();
-  auto data = ReadFile(png_file);
+  scoped_refptr<SharedBuffer> data = ReadFile(png_file);
   ASSERT_FALSE(data->IsEmpty());
   ASSERT_LT(bytes_needed_to_decode_size, data->size());
 
@@ -150,7 +150,7 @@
                                    size_t offset,
                                    size_t length) {
   auto decoder = CreatePNGDecoder();
-  auto data = ReadFile(png_file);
+  scoped_refptr<SharedBuffer> data = ReadFile(png_file);
   ASSERT_FALSE(data->IsEmpty());
 
   scoped_refptr<SharedBuffer> invalid_data =
@@ -182,7 +182,7 @@
                          size_t offset_fctl,
                          size_t expected_frame_count,
                          bool should_fail) {
-  auto data = ReadFile(png_file);
+  scoped_refptr<SharedBuffer> data = ReadFile(png_file);
   ASSERT_FALSE(data->IsEmpty());
 
   auto decoder = CreatePNGDecoder();
@@ -227,7 +227,7 @@
 void TestProgressiveDecodingContinuesAfterFullData(
     const char* png_file,
     size_t offset_mid_first_frame) {
-  auto full_data = ReadFile(png_file);
+  scoped_refptr<SharedBuffer> full_data = ReadFile(png_file);
   ASSERT_FALSE(full_data->IsEmpty());
 
   auto decoder_upfront = CreatePNGDecoder();
@@ -339,7 +339,7 @@
   size_t frame_offsets[kExpectedFrameCount] = {141, 249, 322, 430};
 
   auto decoder = CreatePNGDecoder();
-  auto data = ReadFile(png_file);
+  scoped_refptr<SharedBuffer> data = ReadFile(png_file);
   ASSERT_FALSE(data->IsEmpty());
   size_t frames_parsed = 0;
 
@@ -412,7 +412,7 @@
   const char* png_file =
       "/LayoutTests/images/resources/"
       "png-animated-idat-part-of-animation.png";
-  auto data = ReadFile(png_file);
+  scoped_refptr<SharedBuffer> data = ReadFile(png_file);
   ASSERT_FALSE(data->IsEmpty());
 
   const size_t kOffsetActl = 33u;
@@ -463,7 +463,7 @@
       "/LayoutTests/images/resources/"
       "cHRM_color_spin.png";
   {
-    auto data2 = ReadFile(png_file);
+    scoped_refptr<SharedBuffer> data2 = ReadFile(png_file);
     ASSERT_FALSE(data2->IsEmpty());
     const size_t kPostIDATOffset = 30971u;
     for (size_t times = 0; times < 2; times++) {
@@ -489,7 +489,7 @@
   const char* png_file =
       "/LayoutTests/images/resources/"
       "png-animated-idat-not-part-of-animation.png";
-  auto data = ReadFile(png_file);
+  scoped_refptr<SharedBuffer> data = ReadFile(png_file);
   ASSERT_FALSE(data->IsEmpty());
 
   // Insert fcTL and fdAT prior to the IDAT
@@ -544,7 +544,7 @@
   const char* png_file =
       "/LayoutTests/images/resources/"
       "png-animated-idat-part-of-animation.png";
-  auto data = ReadFile(png_file);
+  scoped_refptr<SharedBuffer> data = ReadFile(png_file);
   ASSERT_FALSE(data->IsEmpty());
 
   const size_t kFctlOffset = 95u;
@@ -573,7 +573,7 @@
   const char* png_file =
       "/LayoutTests/images/resources/"
       "png-animated-idat-part-of-animation.png";
-  auto data = ReadFile(png_file);
+  scoped_refptr<SharedBuffer> data = ReadFile(png_file);
   auto decoder = CreatePNGDecoder();
   ASSERT_FALSE(data->IsEmpty());
 
@@ -779,7 +779,7 @@
       "/LayoutTests/images/resources/"
       "png-animated-three-independent-frames.png";
   auto decoder = CreatePNGDecoder();
-  auto full_data = ReadFile(png_file);
+  scoped_refptr<SharedBuffer> full_data = ReadFile(png_file);
   ASSERT_FALSE(full_data->IsEmpty());
 
   // 160u is a randomly chosen offset in the IDAT chunk of the first frame.
@@ -814,7 +814,7 @@
   const char* png_file =
       "/LayoutTests/images/resources/"
       "png-animated-idat-part-of-animation.png";
-  auto original_data = ReadFile(png_file);
+  scoped_refptr<SharedBuffer> original_data = ReadFile(png_file);
   ASSERT_FALSE(original_data->IsEmpty());
 
   // This file almost fits the bill. Modify it to dispose frame 0, making
@@ -868,7 +868,7 @@
   const char* png_file =
       "/LayoutTests/images/resources/"
       "png-animated-idat-not-part-of-animation.png";
-  auto original_data = ReadFile(png_file);
+  scoped_refptr<SharedBuffer> original_data = ReadFile(png_file);
   ASSERT_FALSE(original_data->IsEmpty());
 
   const size_t kFcTLOffset = 2519u;
@@ -894,6 +894,72 @@
   TestByteByByteDecode(CreatePNGDecoder, data.get(), 1, kAnimationNone);
 }
 
+TEST(AnimatedPNGTests, Offset) {
+  const char* png_file = "/LayoutTests/images/resources/apng18.png";
+  scoped_refptr<SharedBuffer> original_data = ReadFile(png_file);
+  ASSERT_FALSE(original_data->IsEmpty());
+
+  Vector<unsigned> baseline_hashes;
+  CreateDecodingBaseline(CreatePNGDecoder, original_data.get(),
+                         &baseline_hashes);
+  constexpr size_t kExpectedFrameCount = 13;
+  ASSERT_EQ(kExpectedFrameCount, baseline_hashes.size());
+
+  constexpr size_t kOffset = 37;
+  char buffer[kOffset] = {};
+
+  auto data = SharedBuffer::Create(buffer, kOffset);
+  data->Append(*original_data.get());
+
+  // Use the same defaults as CreatePNGDecoder, except use the (arbitrary)
+  // non-zero offset.
+  auto decoder = std::make_unique<PNGImageDecoder>(
+      ImageDecoder::kAlphaNotPremultiplied, ColorBehavior::TransformToSRGB(),
+      ImageDecoder::kNoDecodedImageByteLimit, kOffset);
+  decoder->SetData(data, true);
+  ASSERT_EQ(kExpectedFrameCount, decoder->FrameCount());
+
+  for (size_t i = 0; i < kExpectedFrameCount; ++i) {
+    auto* frame = decoder->DecodeFrameBufferAtIndex(i);
+    EXPECT_EQ(baseline_hashes[i], HashBitmap(frame->Bitmap()));
+  }
+}
+
+TEST(AnimatedPNGTests, ExtraChunksBeforeIHDR) {
+  const char* png_file = "/LayoutTests/images/resources/apng18.png";
+  scoped_refptr<SharedBuffer> original_data = ReadFile(png_file);
+  ASSERT_FALSE(original_data->IsEmpty());
+
+  Vector<unsigned> baseline_hashes;
+  CreateDecodingBaseline(CreatePNGDecoder, original_data.get(),
+                         &baseline_hashes);
+  constexpr size_t kExpectedFrameCount = 13;
+  ASSERT_EQ(kExpectedFrameCount, baseline_hashes.size());
+
+  constexpr size_t kPngSignatureSize = 8;
+  auto data = SharedBuffer::Create(original_data->Data(), kPngSignatureSize);
+
+  // Arbitrary chunk of data.
+  constexpr size_t kExtraChunkSize = 13;
+  constexpr png_byte kExtraChunk[kExtraChunkSize] = {
+      0, 0, 0, 1, 't', 'R', 'c', 'N', 68, 82, 0, 87, 10};
+  data->Append(reinterpret_cast<const char*>(kExtraChunk), kExtraChunkSize);
+
+  // Append the rest of the data from the original.
+  data->Append(original_data->Data() + kPngSignatureSize,
+               original_data->size() - kPngSignatureSize);
+  ASSERT_EQ(original_data->size() + kExtraChunkSize, data->size());
+
+  auto decoder = CreatePNGDecoder();
+  decoder->SetData(data, true);
+  ASSERT_EQ(kExpectedFrameCount, decoder->FrameCount());
+
+  for (size_t i = 0; i < kExpectedFrameCount; ++i) {
+    auto* frame = decoder->DecodeFrameBufferAtIndex(i);
+    EXPECT_EQ(baseline_hashes[i], HashBitmap(frame->Bitmap()));
+  }
+}
+
 // Static PNG tests
 
 TEST(StaticPNGTests, repetitionCountTest) {
@@ -946,7 +1012,7 @@
       {"/LayoutTests/images/resources/lenna.png", 1u, 40000u},
   };
   for (const auto& rec : g_recs) {
-    auto full_data = ReadFile(rec.name);
+    scoped_refptr<SharedBuffer> full_data = ReadFile(rec.name);
     ASSERT_TRUE(full_data.get());
 
     // Create with enough data for part of the first frame.
diff --git a/third_party/WebKit/Source/platform/image-decoders/png/PNGImageReader.cpp b/third_party/WebKit/Source/platform/image-decoders/png/PNGImageReader.cpp
index 6fe439e..8e4c18d 100644
--- a/third_party/WebKit/Source/platform/image-decoders/png/PNGImageReader.cpp
+++ b/third_party/WebKit/Source/platform/image-decoders/png/PNGImageReader.cpp
@@ -81,6 +81,7 @@
       initial_offset_(initial_offset),
       read_offset_(initial_offset),
       progressive_decode_offset_(0),
+      ihdr_offset_(0),
       idat_offset_(0),
       idat_is_part_of_animation_(false),
       expect_idats_(true),
@@ -197,32 +198,35 @@
 
 void PNGImageReader::StartFrameDecoding(const FastSharedBufferReader& reader,
                                         size_t index) {
-  // If the frame is the size of the whole image, just re-process all header
-  // data up to the first frame.
+  DCHECK_GT(ihdr_offset_, initial_offset_);
+  ProcessData(reader, initial_offset_, ihdr_offset_ - initial_offset_);
+
   const IntRect& frame_rect = frame_info_[index].frame_rect;
   if (frame_rect == IntRect(0, 0, width_, height_)) {
-    ProcessData(reader, initial_offset_, idat_offset_);
+    DCHECK_GT(idat_offset_, ihdr_offset_);
+    ProcessData(reader, ihdr_offset_, idat_offset_ - ihdr_offset_);
     return;
   }
 
   // Process the IHDR chunk, but change the width and height so it reflects
   // the frame's width and height. ImageDecoder will apply the x,y offset.
-  constexpr size_t kHeaderSize = kBufferSize;
+  constexpr size_t kHeaderSize = 25;
   char read_buffer[kHeaderSize];
   const png_byte* chunk =
-      ReadAsConstPngBytep(reader, initial_offset_, kHeaderSize, read_buffer);
+      ReadAsConstPngBytep(reader, ihdr_offset_, kHeaderSize, read_buffer);
   png_byte* header = reinterpret_cast<png_byte*>(read_buffer);
   if (chunk != header)
     memcpy(header, chunk, kHeaderSize);
-  png_save_uint_32(header + 16, frame_rect.Width());
-  png_save_uint_32(header + 20, frame_rect.Height());
+  png_save_uint_32(header +  8, frame_rect.Width());
+  png_save_uint_32(header + 12, frame_rect.Height());
   // IHDR has been modified, so tell libpng to ignore CRC errors.
   png_set_crc_action(png_, PNG_CRC_QUIET_USE, PNG_CRC_QUIET_USE);
   png_process_data(png_, info_, header, kHeaderSize);
 
   // Process the rest of the header chunks.
-  ProcessData(reader, initial_offset_ + kHeaderSize,
-              idat_offset_ - kHeaderSize);
+  DCHECK_GE(idat_offset_, ihdr_offset_ + kHeaderSize);
+  ProcessData(reader, ihdr_offset_ + kHeaderSize,
+              idat_offset_ - ihdr_offset_ - kHeaderSize);
 }
 
 // Determine if the bytes 4 to 7 of |chunk| indicate that it is a |tag| chunk.
@@ -598,6 +602,7 @@
       ProcessData(reader, read_offset_ + 8, length + 4);
       if (IsChunk(chunk, "IHDR")) {
         parsed_ihdr_ = true;
+        ihdr_offset_ = read_offset_;
         width_ = png_get_image_width(png_, info_);
         height_ = png_get_image_height(png_, info_);
       }
diff --git a/third_party/WebKit/Source/platform/image-decoders/png/PNGImageReader.h b/third_party/WebKit/Source/platform/image-decoders/png/PNGImageReader.h
index 2dd2e32..f2d26df 100644
--- a/third_party/WebKit/Source/platform/image-decoders/png/PNGImageReader.h
+++ b/third_party/WebKit/Source/platform/image-decoders/png/PNGImageReader.h
@@ -115,6 +115,7 @@
   // How many bytes have been read during parsing.
   size_t read_offset_;
   size_t progressive_decode_offset_;
+  size_t ihdr_offset_;
   size_t idat_offset_;
 
   bool idat_is_part_of_animation_;
diff --git a/third_party/WebKit/Source/platform/runtime_enabled_features.json5 b/third_party/WebKit/Source/platform/runtime_enabled_features.json5
index 3420746f..469d383 100644
--- a/third_party/WebKit/Source/platform/runtime_enabled_features.json5
+++ b/third_party/WebKit/Source/platform/runtime_enabled_features.json5
@@ -903,7 +903,9 @@
       status: "stable",
     },
     // Handles frame scrolling via the root PaintLayer instead of the FrameView.
-    // crbug.com/417782 tracks enabling this by default.
+    // The master bug for the root layer scrolling project is crbug.com/417782.
+    // This REF is enabled iff features::kRootLayerScrolling is on or
+    // experimental web platform features are enabled.
     {
       name: "RootLayerScrolling",
       status: "stable",
diff --git a/third_party/WebKit/Source/platform/wtf/HashTable.h b/third_party/WebKit/Source/platform/wtf/HashTable.h
index b6b4d29..7f1bc045 100644
--- a/third_party/WebKit/Source/platform/wtf/HashTable.h
+++ b/third_party/WebKit/Source/platform/wtf/HashTable.h
@@ -2124,7 +2124,6 @@
     if (IsTraceableInCollectionTrait<Traits>::value) {
 #if DCHECK_IS_ON()
       DCHECK(!Allocator::WeakTableRegistered(visitor, this) || Enqueued());
-      DCHECK(!Enqueued() || Allocator::WeakTableRegistered(visitor, this));
 #endif
 
       // Mix of strong and weak fields. We use an approach similar to ephemeron
@@ -2133,15 +2132,17 @@
       // - http://www.jucs.org/jucs_14_21/eliminating_cycles_in_weak
       // Adding the table for ephemeron marking delays marking any elements in
       // the backing until regular marking is finished.
-      if (!Enqueued()) {
-        Allocator::RegisterWeakTable(
-            visitor, this,
-            WeakProcessingHashTableHelper<
-                Traits::kWeakHandlingFlag, Key, Value, Extractor, HashFunctions,
-                Traits, KeyTraits, Allocator>::EphemeronIteration,
-            WeakProcessingHashTableHelper<
-                Traits::kWeakHandlingFlag, Key, Value, Extractor, HashFunctions,
-                Traits, KeyTraits, Allocator>::EphemeronIterationDone);
+      if (!Enqueued() &&
+          Allocator::RegisterWeakTable(
+              visitor, this,
+              WeakProcessingHashTableHelper<Traits::kWeakHandlingFlag, Key,
+                                            Value, Extractor, HashFunctions,
+                                            Traits, KeyTraits,
+                                            Allocator>::EphemeronIteration,
+              WeakProcessingHashTableHelper<
+                  Traits::kWeakHandlingFlag, Key, Value, Extractor,
+                  HashFunctions, Traits, KeyTraits,
+                  Allocator>::EphemeronIterationDone)) {
         SetEnqueued();
       }
     }
diff --git a/third_party/WebKit/common/mime_util/OWNERS b/third_party/WebKit/common/mime_util/OWNERS
index ca45491..10342b0 100644
--- a/third_party/WebKit/common/mime_util/OWNERS
+++ b/third_party/WebKit/common/mime_util/OWNERS
@@ -1,5 +1,4 @@
 asanka@chromium.org
-cbentzel@chromium.org
 eroman@chromium.org
 mmenke@chromium.org
 rsleevi@chromium.org
diff --git a/third_party/WebKit/public/common/mime_util/OWNERS b/third_party/WebKit/public/common/mime_util/OWNERS
index ca45491..10342b0 100644
--- a/third_party/WebKit/public/common/mime_util/OWNERS
+++ b/third_party/WebKit/public/common/mime_util/OWNERS
@@ -1,5 +1,4 @@
 asanka@chromium.org
-cbentzel@chromium.org
 eroman@chromium.org
 mmenke@chromium.org
 rsleevi@chromium.org
diff --git a/third_party/WebKit/public/platform/WebComputedAXTree.h b/third_party/WebKit/public/platform/WebComputedAXTree.h
index e91a919..840b49a 100644
--- a/third_party/WebKit/public/platform/WebComputedAXTree.h
+++ b/third_party/WebKit/public/platform/WebComputedAXTree.h
@@ -22,6 +22,13 @@
   AOM_ATTR_SET_SIZE,
 };
 
+enum WebAOMFloatAttribute {
+  AOM_FLOAT_ATTRIBUTE_NONE,
+  AOM_ATTR_VALUE_MIN,
+  AOM_ATTR_VALUE_MAX,
+  AOM_ATTR_VALUE_NOW,
+};
+
 enum WebAOMStringAttribute {
   AOM_STRING_ATTRIBUTE_NONE,
   AOM_ATTR_AUTOCOMPLETE,
@@ -64,6 +71,10 @@
                                            WebAOMStringAttribute,
                                            WebString* out_param) = 0;
 
+  virtual bool GetFloatAttributeForAXNode(int32_t ax_id,
+                                          WebAOMFloatAttribute,
+                                          float* out_param) = 0;
+
   // The role is stored seperately from other attributes in the AXNode, so we
   // expose a seperate method for retrieving this.
   virtual bool GetRoleForAXNode(int32_t ax_id, blink::WebString* out_param) = 0;
diff --git a/third_party/WebKit/public/platform/modules/credentialmanager/credential_manager.mojom b/third_party/WebKit/public/platform/modules/credentialmanager/credential_manager.mojom
index a300f3b..42c1b57 100644
--- a/third_party/WebKit/public/platform/modules/credentialmanager/credential_manager.mojom
+++ b/third_party/WebKit/public/platform/modules/credentialmanager/credential_manager.mojom
@@ -26,7 +26,6 @@
   NOT_ALLOWED,
   NOT_SUPPORTED,
   INVALID_DOMAIN,
-  TIMED_OUT,
   NOT_IMPLEMENTED,
   UNKNOWN
 };
diff --git a/third_party/WebKit/public/platform/modules/notifications/notification_service.mojom b/third_party/WebKit/public/platform/modules/notifications/notification_service.mojom
index 6796904..daf37584 100644
--- a/third_party/WebKit/public/platform/modules/notifications/notification_service.mojom
+++ b/third_party/WebKit/public/platform/modules/notifications/notification_service.mojom
@@ -21,6 +21,15 @@
   OnClose();
 };
 
+// Possible things that can go wrong when displaying a notification created by
+// a service worker, which we may wish to distinguish between on the renderer
+// side to provide a more useful error message to developers.
+enum PersistentNotificationError {
+  NONE,
+  INTERNAL_ERROR,
+  PERMISSION_DENIED,
+};
+
 // Service through which Blink can request notifications to be shown, closed or
 // retrieved from the embedder.
 interface NotificationService {
@@ -46,4 +55,15 @@
   // Calling this without previously calling DisplayNonPersistentNotification
   // with the same token is a no-op.
   CloseNonPersistentNotification(string token);
+
+  // Shows a notification that *is* associated with a service worker.
+  // The service worker identified by |service_worker_registration_id| will be
+  // notified when the notification is clicked/closed.
+  // Responds once the notification is successfully processed or rejected.
+  DisplayPersistentNotification(
+        int64 service_worker_registration_id,
+        NotificationData notification_data,
+        NotificationResources notification_resources)
+                => (PersistentNotificationError error);
+
 };
diff --git a/third_party/WebKit/public/platform/modules/webauth/authenticator.mojom b/third_party/WebKit/public/platform/modules/webauth/authenticator.mojom
index 8f742236..26c0993 100644
--- a/third_party/WebKit/public/platform/modules/webauth/authenticator.mojom
+++ b/third_party/WebKit/public/platform/modules/webauth/authenticator.mojom
@@ -18,7 +18,6 @@
   NOT_ALLOWED_ERROR,
   NOT_SUPPORTED_ERROR,
   INVALID_DOMAIN,
-  TIMED_OUT,
   NOT_IMPLEMENTED,
   UNKNOWN_ERROR,
 };
diff --git a/third_party/custom_tabs_client/BUILD.gn b/third_party/custom_tabs_client/BUILD.gn
index 86b76fe..812a97f9 100644
--- a/third_party/custom_tabs_client/BUILD.gn
+++ b/third_party/custom_tabs_client/BUILD.gn
@@ -15,6 +15,7 @@
 
 android_resources("custom_tabs_support_resources") {
   resource_dirs = [ "src/customtabs/res" ]
+  android_manifest = "src/customtabs/AndroidManifest.xml"
   custom_package = "android.support.customtabs.browseractions"
 }
 
@@ -60,6 +61,8 @@
     "src/customtabs/src/android/support/customtabs/browseractions/BrowserActionsFallbackMenuUi.java",
     "src/customtabs/src/android/support/customtabs/browseractions/BrowserActionsFallbackMenuView.java",
     "src/customtabs/src/android/support/customtabs/browseractions/BrowserActionsIntent.java",
+    "src/customtabs/src/android/support/customtabs/browseractions/BrowserServiceFileProvider.java",
+    "src/customtabs/src/android/support/customtabs/browseractions/BrowserServiceImageReadTask.java",
     "src/customtabs/src/android/support/customtabs/CustomTabsCallback.java",
     "src/customtabs/src/android/support/customtabs/CustomTabsClient.java",
     "src/customtabs/src/android/support/customtabs/CustomTabsIntent.java",
@@ -78,6 +81,7 @@
     "//third_party/android_tools:android_support_v7_appcompat_java",
   ]
   srcjar_deps = [ ":chrome_custom_tabs_service_aidl" ]
+  android_manifest_for_lint = "src/customtabs/AndroidManifest.xml"
   chromium_code = true
 }
 
diff --git a/third_party/node/node.py b/third_party/node/node.py
index 0194f561..8097e2c 100755
--- a/third_party/node/node.py
+++ b/third_party/node/node.py
@@ -24,7 +24,6 @@
   stdout, stderr = process.communicate()
 
   if stderr:
-    print >> sys.stderr, '%s failed: %s' % (cmd, stderr)
-    raise
+    raise RuntimeError('%s failed: %s' % (cmd, stderr))
 
   return stdout
diff --git a/third_party/ots/README.chromium b/third_party/ots/README.chromium
index 44f1683..8e8aaba8 100644
--- a/third_party/ots/README.chromium
+++ b/third_party/ots/README.chromium
@@ -10,3 +10,5 @@
 - src/ots.cc: Changed include path to woff2_dec.h.
 - BUILD.gn: Added.
 - fuzz/: Added.
+- ots.cc: Allow CFF2 outlines, upstreamed in
+  https://github.com/khaledhosny/ots/pull/161
\ No newline at end of file
diff --git a/third_party/ots/src/ots.cc b/third_party/ots/src/ots.cc
index f6020143..6b14272 100644
--- a/third_party/ots/src/ots.cc
+++ b/third_party/ots/src/ots.cc
@@ -686,7 +686,7 @@
     }
   }
 
-  if (font->GetTable(OTS_TAG_CFF)) {
+  if (font->GetTable(OTS_TAG_CFF) || font->GetTable(OTS_TAG('C', 'F', 'F', '2'))) {
     // font with PostScript glyph
     if (font->version != OTS_TAG('O','T','T','O')) {
       return OTS_FAILURE_MSG_HDR("wrong font version for PostScript glyph data");
diff --git a/third_party/zlib/OWNERS b/third_party/zlib/OWNERS
index 07d8bff..069bcd8c 100644
--- a/third_party/zlib/OWNERS
+++ b/third_party/zlib/OWNERS
@@ -1,7 +1,6 @@
 agl@chromium.org
 cavalcantii@chromium.org
 cblume@chromium.org
-gavinp@chromium.org
 mtklein@chromium.org
 scroggo@chromium.org
 
diff --git a/tools/determinism/deterministic_build_whitelist.pyl b/tools/determinism/deterministic_build_whitelist.pyl
index dad8a92..9df92553 100644
--- a/tools/determinism/deterministic_build_whitelist.pyl
+++ b/tools/determinism/deterministic_build_whitelist.pyl
@@ -118,9 +118,7 @@
     'message_center_unittests',
     'midi_unittests',
     'mojo_common_unittests',
-    'mojo_public_bindings_unittests',
-    'mojo_public_system_unittests',
-    'mojo_system_unittests',
+    'mojo_unittests',
     'nacl_loader_unittests',
     'native_theme_unittests',
     'net_unittests',
@@ -274,10 +272,8 @@
     'mksnapshot.exe',
     'mojo_message_pipe_perftests.exe',
     'mojo_public_bindings_perftests.exe',
-    'mojo_public_bindings_unittests.exe',
     'mojo_public_system_perftests.exe',
-    'mojo_public_system_unittests.exe',
-    'mojo_system_unittests.exe',
+    'mojo_unittests.exe',
     'mus_demo_library.dll',
     'nacl_irt_x86_32.nexe',
     'nacl_irt_x86_64.nexe',
diff --git a/tools/fuchsia/local-sdk.py b/tools/fuchsia/local-sdk.py
index b99e786..7667caa 100755
--- a/tools/fuchsia/local-sdk.py
+++ b/tools/fuchsia/local-sdk.py
@@ -34,9 +34,16 @@
 def BuildForArch(project, arch):
   Run('scripts/build-zircon.sh', '-p', project)
   Run('build/gn/gen.py', '--target_cpu=' + arch,
-      '--packages=garnet/packages/sdk', '--release',
-      '--args=bootfs_packages=true')
+      '--packages=garnet/packages/sdk', '--release')
   Run('buildtools/ninja', '-C', 'out/release-' + arch)
+  # Also build the deprecated bootfs-based image.
+  # TODO(crbug.com/805057): Remove this once the bootfs path is turned down.
+  build_dir_bootfs = 'out/release-' + arch + '-bootfs'
+  Run('build/gn/gen.py', '--target_cpu=' + arch,
+      '--packages=garnet/packages/sdk_bootfs', '--release',
+      '--args=bootfs_packages=true',
+      '--build-dir='+build_dir_bootfs)
+  Run('buildtools/ninja', '-C', build_dir_bootfs)
 
 
 def main(args):
diff --git a/tools/ipc_fuzzer/fuzzer/fuzzer.cc b/tools/ipc_fuzzer/fuzzer/fuzzer.cc
index a986bb9..aef36078 100644
--- a/tools/ipc_fuzzer/fuzzer/fuzzer.cc
+++ b/tools/ipc_fuzzer/fuzzer/fuzzer.cc
@@ -1447,8 +1447,9 @@
     if (!fuzzer->ShouldGenerate())
       return true;
 
-    switch (RandInRange(4)) {
-      case network::DataElement::Type::TYPE_BYTES: {
+    switch (RandInRange(3)) {
+      case 0: {
+        // network::DataElement::Type::TYPE_BYTES
         if (RandEvent(2)) {
           p->SetToEmptyBytes();
         } else {
@@ -1459,7 +1460,8 @@
         }
         return true;
       }
-      case network::DataElement::Type::TYPE_FILE: {
+      case 1: {
+        // network::DataElement::Type::TYPE_FILE
         base::FilePath path;
         uint64_t offset;
         uint64_t length;
@@ -1475,7 +1477,8 @@
         p->SetToFilePathRange(path, offset, length, modification_time);
         return true;
       }
-      case network::DataElement::Type::TYPE_BLOB: {
+      case 2: {
+        // network::DataElement::Type::TYPE_BLOB
         std::string uuid;
         uint64_t offset;
         uint64_t length;
diff --git a/tools/mb/mb.py b/tools/mb/mb.py
index 7337d2c..e4ce1a11 100755
--- a/tools/mb/mb.py
+++ b/tools/mb/mb.py
@@ -383,6 +383,8 @@
       return []
 
     # This code is naive and just picks reasonable defaults per platform.
+    # TODO(thakis): This assumes that host platform is the same as
+    # target platform.
     if self.platform == 'darwin':
       os_dim = ('os', 'Mac-10.12')
     elif self.platform.startswith('linux'):
@@ -726,6 +728,7 @@
 
     android = 'target_os="android"' in vals['gn_args']
     fuchsia = 'target_os="fuchsia"' in vals['gn_args']
+    win = self.platform == 'win32' or 'target_os="win"' in vals['gn_args']
     for target in swarming_targets:
       if android:
         # Android targets may be either android_apk or executable. The former
@@ -749,11 +752,11 @@
         label = isolate_map[target]['label']
         runtime_deps_targets = [
             'obj/%s.stamp.runtime_deps' % label.replace(':', '/')]
-        if self.platform == 'win32':
+        if win:
           runtime_deps_targets += [ target + '.exe.runtime_deps' ]
         else:
           runtime_deps_targets += [ target + '.runtime_deps' ]
-      elif self.platform == 'win32':
+      elif win:
         runtime_deps_targets = [target + '.exe.runtime_deps']
       else:
         runtime_deps_targets = [target + '.runtime_deps']
@@ -905,6 +908,7 @@
 
     is_android = 'target_os="android"' in vals['gn_args']
     is_fuchsia = 'target_os="fuchsia"' in vals['gn_args']
+    is_win = self.platform == 'win32' or 'target_os="win"' in vals['gn_args']
 
     # This should be true if tests with type='windowed_test_launcher' are
     # expected to run using xvfb. For example, Linux Desktop, X11 CrOS and
@@ -923,7 +927,7 @@
     test_type = isolate_map[target]['type']
 
     executable = isolate_map[target].get('executable', target)
-    executable_suffix = '.exe' if self.platform == 'win32' else ''
+    executable_suffix = '.exe' if is_win else ''
 
     cmdline = []
     extra_files = [
diff --git a/tools/metrics/histograms/enums.xml b/tools/metrics/histograms/enums.xml
index dbf6a7f..c461325 100644
--- a/tools/metrics/histograms/enums.xml
+++ b/tools/metrics/histograms/enums.xml
@@ -25432,6 +25432,7 @@
   <int value="-2114831248" label="disable-new-ntp"/>
   <int value="-2113705745"
       label="CrossOriginMediaPlaybackRequiresUserGesture:enabled"/>
+  <int value="-2099457894" label="Mash:enabled"/>
   <int value="-2099035488" label="enable-data-reduction-proxy-bypass-warning"/>
   <int value="-2098610409" label="disable-lcd-text"/>
   <int value="-2098243118" label="OmniboxTailSuggestions:disabled"/>
@@ -25448,6 +25449,7 @@
   <int value="-2075807193" label="enable-webusb-on-any-origin"/>
   <int value="-2075725205" label="disable-new-zip-unpacker"/>
   <int value="-2067166422" label="enable-slimming-paint-v2"/>
+  <int value="-2066541315" label="Mus:enabled"/>
   <int value="-2064164557" label="DownloadsForeground:disabled"/>
   <int value="-2063352474" label="enable-new-task-manager"/>
   <int value="-2063014275" label="enable-web-bluetooth"/>
@@ -25571,6 +25573,7 @@
   <int value="-1847835522" label="disable-touch-adjustment"/>
   <int value="-1847776781"
       label="enable-loading-ipc-optimization-for-small-resources"/>
+  <int value="-1844754731" label="Mash:disabled"/>
   <int value="-1839874877" label="WebXROrientationSensorDevice:enabled"/>
   <int value="-1839496458" label="disable-file-manager-touch-mode"/>
   <int value="-1838482444" label="disable-settings-window"/>
@@ -26260,6 +26263,7 @@
   <int value="-122492389" label="enable-browser-task-scheduler"/>
   <int value="-120521482" label="DirectManipulationStylus:enabled"/>
   <int value="-119055644" label="GenericSensor:enabled"/>
+  <int value="-115834377" label="EnableUnifiedMultiDeviceSetup:disabled"/>
   <int value="-112459802" label="WebXrRenderPath:enabled"/>
   <int value="-110756896"
       label="NTPArticleSuggestionsExpandableHeader:enabled"/>
@@ -26447,6 +26451,7 @@
   <int value="379428799" label="security-chip-animation"/>
   <int value="385969127" label="disable-win32k-lockdown"/>
   <int value="387178525" label="VideoFullscreenOrientationLock:enabled"/>
+  <int value="388786873" label="EnableUnifiedMultiDeviceSettings:enabled"/>
   <int value="388996324" label="CustomContextMenu:disabled"/>
   <int value="393704200" label="account-consistency"/>
   <int value="398903399" label="GenericSensorExtraClasses:disabled"/>
@@ -26507,6 +26512,7 @@
   <int value="538468149" label="OfflinePagesCT:enabled"/>
   <int value="546520086" label="enable-data-reduction-proxy-savings-promo"/>
   <int value="546710806" label="disable-easy-signin"/>
+  <int value="549483647" label="EnableUnifiedMultiDeviceSettings:disabled"/>
   <int value="550378029" label="reset-app-list-install-state"/>
   <int value="550387510" label="NTPAssetDownloadSuggestions:disabled"/>
   <int value="558873715" label="SiteDetails:disabled"/>
@@ -26738,6 +26744,7 @@
   <int value="1113365156" label="tab-management-experiment-type-chive"/>
   <int value="1114629582" label="enable-floating-virtual-keyboard"/>
   <int value="1115476442" label="PolicyTool:disabled"/>
+  <int value="1115635149" label="EnableUnifiedMultiDeviceSetup:enabled"/>
   <int value="1116593018" label="CaptureThumbnailOnLoadFinished:disabled"/>
   <int value="1118109174" label="enable-launcher-search-provider-api"/>
   <int value="1126061778" label="CaptureThumbnailOnLoadFinished:enabled"/>
@@ -26788,6 +26795,7 @@
   <int value="1220464509" label="enable-first-run-ui-transitions"/>
   <int value="1221559505" label="enable-spelling-feedback-field-trial"/>
   <int value="1222017136" label="WebRtcUseEchoCanceller3:disabled"/>
+  <int value="1226624874" label="Mus:disabled"/>
   <int value="1235800887" label="V8Ignition:enabled"/>
   <int value="1235940786" label="ChromeHomePersistentIph:enabled"/>
   <int value="1237297772" label="no-pings"/>
@@ -45474,6 +45482,9 @@
   <int value="0" label="Error instantiating variable font, falling back"/>
   <int value="1" label="Success conventional web font"/>
   <int value="2" label="Success variable web font"/>
+  <int value="3" label="Success CBDT/CBLC color font"/>
+  <int value="4" label="Success CFF2 outline font"/>
+  <int value="5" label="Success Sbix color font"/>
 </enum>
 
 <enum name="WebFontInterventionResult">
diff --git a/tools/metrics/histograms/histograms.xml b/tools/metrics/histograms/histograms.xml
index 45a193e..06b09e1 100644
--- a/tools/metrics/histograms/histograms.xml
+++ b/tools/metrics/histograms/histograms.xml
@@ -7306,6 +7306,10 @@
 </histogram>
 
 <histogram name="Blink.PaintInvalidation.UpdateTime" units="microseconds">
+  <obsolete>
+    SlimmingPaintInvalidation is enabled by default, so this histogram is no
+    longer being logged.
+  </obsolete>
   <owner>paint-dev@chromium.org</owner>
   <summary>
     Time spent updating paint invalidation in the Blink document lifecycle. Not
diff --git a/tools/metrics/ukm/ukm.xml b/tools/metrics/ukm/ukm.xml
index 0ee402a..637de43 100644
--- a/tools/metrics/ukm/ukm.xml
+++ b/tools/metrics/ukm/ukm.xml
@@ -2140,6 +2140,12 @@
       blockable document flavours: html, xml, json, plain.
     </summary>
   </metric>
+  <metric name="ContentLengthWasZero">
+    <summary>
+      ContentLengthWasZero=1 if Content-Length was available and equal to 0.
+      ContentLengthWasZero=0 otherwise.
+    </summary>
+  </metric>
   <metric name="ContentResourceType">
     <summary>
       content::ResourceType of the blocked resource (e.g. image / script / xhr /
diff --git a/tools/perf/core/results_dashboard.py b/tools/perf/core/results_dashboard.py
index 596525e..2e626ae 100755
--- a/tools/perf/core/results_dashboard.py
+++ b/tools/perf/core/results_dashboard.py
@@ -20,18 +20,20 @@
 import urllib
 import urllib2
 
+from core import path_util
+
 # The paths in the results dashboard URLs for sending and viewing results.
 SEND_RESULTS_PATH = '/add_point'
 SEND_HISTOGRAMS_PATH = '/add_histograms'
 RESULTS_LINK_PATH = '/report?masters=%s&bots=%s&tests=%s&rev=%s'
 
-# CACHE_DIR/CACHE_FILENAME will be created in options.build_dir to cache
+# CACHE_DIR/CACHE_FILENAME will be created in a tmp_dir to cache
 # results which need to be retried.
 CACHE_DIR = 'results_dashboard'
 CACHE_FILENAME = 'results_to_retry'
 
 
-def SendResults(data, url, build_dir, json_url_file=None,
+def SendResults(data, url, tmp_dir, json_url_file=None,
                 send_as_histograms=False, oauth_token=None):
   """Sends results to the Chrome Performance Dashboard.
 
@@ -42,7 +44,7 @@
   Args:
     data: The data to try to send. Must be JSON-serializable.
     url: Performance Dashboard URL (including schema).
-    build_dir: Directory name, where the cache directory shall be.
+    tmp_dir: Directory name, where the cache directory shall be.
     json_url_file: Optional file to which to write the dashboard viewing URL.
     send_as_histograms: True if result is to be sent to /add_histograms.
     oauth_token: string; used for flushing oauth uploads from cache.
@@ -54,7 +56,7 @@
 
   # Write the new request line to the cache file, which contains all lines
   # that we shall try to send now.
-  cache_file_name = _GetCacheFileName(build_dir)
+  cache_file_name = _GetCacheFileName(tmp_dir)
   _AddLineToCacheFile(results_json, cache_file_name)
 
   # Send all the results from this run and the previous cache to the dashboard.
@@ -77,9 +79,9 @@
   return True
 
 
-def _GetCacheFileName(build_dir):
+def _GetCacheFileName(tmp_dir):
   """Gets the cache filename, creating the file if it does not exist."""
-  cache_dir = os.path.join(os.path.abspath(build_dir), CACHE_DIR)
+  cache_dir = os.path.join(os.path.abspath(tmp_dir), CACHE_DIR)
   if not os.path.exists(cache_dir):
     os.makedirs(cache_dir)
   cache_filename = os.path.join(cache_dir, CACHE_FILENAME)
@@ -175,7 +177,7 @@
   return bool(line_dict.get('is_histogramset')), line_dict['data']
 
 
-def MakeHistogramSetWithDiagnostics(histograms_file, chromium_checkout_path,
+def MakeHistogramSetWithDiagnostics(histograms_file,
                                     test_name, bot, buildername, buildnumber,
                                     revisions_dict, is_reference_build,
                                     perf_dashboard_machine_group):
@@ -201,7 +203,7 @@
   add_diagnostics_args = [str(v) for v in add_diagnostics_args]
 
   add_reserved_diagnostics_path = os.path.join(
-      chromium_checkout_path, 'src', 'third_party', 'catapult', 'tracing',
+      path_util.GetChromiumSrcDir(), 'third_party', 'catapult', 'tracing',
       'bin', 'add_reserved_diagnostics')
   cmd = [sys.executable, add_reserved_diagnostics_path] + add_diagnostics_args
 
diff --git a/tools/perf/core/upload_results_to_perf_dashboard.py b/tools/perf/core/upload_results_to_perf_dashboard.py
index 14ea676..bafa71b 100755
--- a/tools/perf/core/upload_results_to_perf_dashboard.py
+++ b/tools/perf/core/upload_results_to_perf_dashboard.py
@@ -10,32 +10,24 @@
 
 import json
 import optparse
-import os
 import re
-import subprocess
 import sys
 
 from core import results_dashboard
 
 
-def _GetMainRevision(commit_pos, build_dir):
+def _GetMainRevision(commit_pos):
   """Return revision to use as the numerical x-value in the perf dashboard.
   This will be used as the value of "rev" in the data passed to
   results_dashboard.SendResults.
   In order or priority, this function could return:
     1. The value of "got_revision_cp" in build properties.
-    3. An SVN number, git commit position, or git commit hash.
   """
-  if commit_pos is not None:
-    return int(re.search(r'{#(\d+)}', commit_pos).group(1))
-  # TODO(sullivan,qyearsley): Don't fall back to _GetRevision if it returns
-  # a git commit, since this should be a numerical revision. Instead, abort
-  # and fail.
-  return _GetRevision(os.path.dirname(os.path.abspath(build_dir)))
+  return int(re.search(r'{#(\d+)}', commit_pos).group(1))
 
 
 def _GetDashboardJson(options):
-  main_revision = _GetMainRevision(options.got_revision_cp, options.build_dir)
+  main_revision = _GetMainRevision(options.got_revision_cp)
   revisions = _GetPerfDashboardRevisionsWithProperties(
     options.got_webrtc_revision, options.got_v8_revision, options.version,
     options.git_revision, main_revision)
@@ -77,8 +69,7 @@
 
 def _GetDashboardHistogramData(options):
   revisions = {
-      '--chromium_commit_positions': _GetMainRevision(
-          options.got_revision_cp, options.build_dir),
+      '--chromium_commit_positions': _GetMainRevision(options.got_revision_cp),
       '--chromium_revisions': options.git_revision
   }
 
@@ -91,7 +82,7 @@
   stripped_test_name = options.name.replace('.reference', '')
 
   return results_dashboard.MakeHistogramSetWithDiagnostics(
-      options.results_file, options.chromium_checkout_dir, stripped_test_name,
+      options.results_file, stripped_test_name,
       options.configuration_name, options.buildername, options.buildnumber,
       revisions, is_reference_build,
       perf_dashboard_machine_group=_GetMachineGroup(options))
@@ -102,9 +93,9 @@
   parser = optparse.OptionParser()
   parser.add_option('--name')
   parser.add_option('--results-file')
+  parser.add_option('--tmp-dir')
   parser.add_option('--output-json-file')
   parser.add_option('--got-revision-cp')
-  parser.add_option('--build-dir')
   parser.add_option('--configuration-name')
   parser.add_option('--results-url')
   parser.add_option('--is-luci-builder', action='store_true', default=False)
@@ -118,7 +109,6 @@
   parser.add_option('--output-json-dashboard-url')
   parser.add_option('--send-as-histograms', action='store_true')
   parser.add_option('--oauth-token-file')
-  parser.add_option('--chromium-checkout-dir')
   return parser
 
 
@@ -150,7 +140,7 @@
     if not results_dashboard.SendResults(
         dashboard_json,
         options.results_url,
-        options.build_dir,
+        options.tmp_dir,
         options.output_json_dashboard_url,
         send_as_histograms=options.send_as_histograms,
         oauth_token=oauth_token):
@@ -166,73 +156,6 @@
   sys.exit(main((sys.argv[1:])))
 
 
-def _GetRevision(in_directory):
-  """Returns the SVN revision, git commit position, or git hash.
-
-  Args:
-    in_directory: A directory in the repository to be checked.
-
-  Returns:
-    An SVN revision as a string if the given directory is in a SVN repository,
-    or a git commit position number, or if that's not available, a git hash.
-    If all of that fails, an empty string is returned.
-  """
-  if not os.path.exists(os.path.join(in_directory, '.svn')):
-    if _IsGitDirectory(in_directory):
-      svn_rev = _GetGitCommitPosition(in_directory)
-      if svn_rev:
-        return svn_rev
-      return _GetGitRevision(in_directory)
-    else:
-      return ''
-
-
-def _IsGitDirectory(dir_path):
-  """Checks whether the given directory is in a git repository.
-
-  Args:
-    dir_path: The directory path to be tested.
-
-  Returns:
-    True if given directory is in a git repository, False otherwise.
-  """
-  git_exe = 'git.bat' if sys.platform.startswith('win') else 'git'
-  with open(os.devnull, 'w') as devnull:
-    p = subprocess.Popen([git_exe, 'rev-parse', '--git-dir'],
-                         cwd=dir_path, stdout=devnull, stderr=devnull)
-    return p.wait() == 0
-
-
-# Regex matching git comment lines containing svn revision info.
-GIT_SVN_ID_RE = re.compile(r'^git-svn-id: .*@([0-9]+) .*$')
-# Regex for the master branch commit position.
-GIT_CR_POS_RE = re.compile(r'^Cr-Commit-Position: refs/heads/master@{#(\d+)}$')
-
-
-def _GetGitCommitPositionFromLog(log):
-  """Returns either the commit position or svn rev from a git log."""
-  # Parse from the bottom up, in case the commit message embeds the message
-  # from a different commit (e.g., for a revert).
-  for r in [GIT_CR_POS_RE, GIT_SVN_ID_RE]:
-    for line in reversed(log.splitlines()):
-      m = r.match(line.strip())
-      if m:
-        return m.group(1)
-  return None
-
-
-def _GetGitCommitPosition(dir_path):
-  """Extracts the commit position or svn revision number of the HEAD commit."""
-  git_exe = 'git.bat' if sys.platform.startswith('win') else 'git'
-  p = subprocess.Popen(
-      [git_exe, 'log', '-n', '1', '--pretty=format:%B', 'HEAD'],
-      cwd=dir_path, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
-  (log, _) = p.communicate()
-  if p.returncode != 0:
-    return None
-  return _GetGitCommitPositionFromLog(log)
-
-
 def _GetPerfDashboardRevisionsWithProperties(
     got_webrtc_revision, got_v8_revision, version, git_revision, main_revision,
     point_id=None):
@@ -250,20 +173,3 @@
     if not versions[key] or versions[key] == 'undefined':
       del versions[key]
   return versions
-
-
-def _GetGitRevision(in_directory):
-  """Returns the git hash tag for the given directory.
-
-  Args:
-    in_directory: The directory where git is to be run.
-
-  Returns:
-    The git SHA1 hash string.
-  """
-  git_exe = 'git.bat' if sys.platform.startswith('win') else 'git'
-  p = subprocess.Popen(
-      [git_exe, 'rev-parse', 'HEAD'],
-      cwd=in_directory, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
-  (stdout, _) = p.communicate()
-  return stdout.strip()
diff --git a/tools/perf/process_perf_results.py b/tools/perf/process_perf_results.py
index 8369338..5ff6b23 100755
--- a/tools/perf/process_perf_results.py
+++ b/tools/perf/process_perf_results.py
@@ -5,7 +5,9 @@
 
 import argparse
 import json
+import shutil
 import sys
+import tempfile
 
 from core import oauth_api
 from core import upload_results_to_perf_dashboard
@@ -17,11 +19,11 @@
 RESULTS_URL = 'https://chromeperf.appspot.com'
 
 def _upload_perf_results(json_to_upload, name, configuration_name,
-    build_properties, oauth_file):
+    build_properties, oauth_file, tmp_dir):
   """Upload the contents of result JSON(s) to the perf dashboard."""
   build_properties = json.loads(build_properties)
   args = [
-      '--build-dir', '/b/c/b/obbs_fyi/src/out',
+      '--tmp-dir', tmp_dir,
       '--buildername', build_properties['buildername'],
       '--buildnumber', build_properties['buildnumber'],
       '--name', name,
@@ -31,7 +33,6 @@
       '--got-revision-cp', build_properties['got_revision_cp'],
       '--got-v8-revision', build_properties['got_v8_revision'],
       '--got-webrtc-revision', build_properties['got_webrtc_revision'],
-      '--chromium-checkout-dir', '/b/c/b/obbs_fyi',
       '--oauth-token-file', oauth_file,
   ]
   if _is_histogram(json_to_upload):
@@ -90,19 +91,24 @@
     ]
 
   test_results_list = []
-  for directory in benchmark_directory_list:
-    if '.reference' in directory:
-      # We don't need to upload reference build data to the
-      # flakiness dashboard since we don't monitor the ref build
-      continue
-    with open(join(directory, 'test_results.json')) as json_data:
-      test_results_list.append(json.load(json_data))
-  _merge_json_output(output_json, test_results_list)
-
-  with oauth_api.with_access_token(service_account_file) as oauth_file:
+  tmpfile_dir = tempfile.mkdtemp('resultscache')
+  try:
     for directory in benchmark_directory_list:
-      _upload_perf_results(join(directory, 'perf_results.json'),
-          directory, configuration_name, build_properties, oauth_file)
+      if '.reference' in directory:
+        # We don't need to upload reference build data to the
+        # flakiness dashboard since we don't monitor the ref build
+        continue
+      with open(join(directory, 'test_results.json')) as json_data:
+        test_results_list.append(json.load(json_data))
+    _merge_json_output(output_json, test_results_list)
+
+    with oauth_api.with_access_token(service_account_file) as oauth_file:
+      for directory in benchmark_directory_list:
+        _upload_perf_results(join(directory, 'perf_results.json'),
+            directory, configuration_name, build_properties,
+            oauth_file, tmpfile_dir)
+  finally:
+    shutil.rmtree(tmpfile_dir)
   return 0
 
 
diff --git a/ui/android/delegated_frame_host_android.cc b/ui/android/delegated_frame_host_android.cc
index 18ccc94..3807b4a 100644
--- a/ui/android/delegated_frame_host_android.cc
+++ b/ui/android/delegated_frame_host_android.cc
@@ -16,6 +16,7 @@
 #include "components/viz/host/host_frame_sink_manager.h"
 #include "components/viz/service/surfaces/surface.h"
 #include "ui/android/view_android.h"
+#include "ui/android/window_android.h"
 #include "ui/android/window_android_compositor.h"
 #include "ui/display/display.h"
 #include "ui/display/screen.h"
@@ -40,14 +41,6 @@
   return layer;
 }
 
-void CopyOutputRequestCallback(
-    scoped_refptr<cc::Layer> readback_layer,
-    viz::CopyOutputRequest::CopyOutputRequestCallback result_callback,
-    std::unique_ptr<viz::CopyOutputResult> copy_output_result) {
-  readback_layer->RemoveFromParent();
-  std::move(result_callback).Run(std::move(copy_output_result));
-}
-
 }  // namespace
 
 DelegatedFrameHostAndroid::DelegatedFrameHostAndroid(
@@ -114,27 +107,52 @@
   return frame_sink_id_;
 }
 
-void DelegatedFrameHostAndroid::RequestCopyOfSurface(
-    WindowAndroidCompositor* compositor,
-    const gfx::Rect& src_subrect_in_pixel,
-    viz::CopyOutputRequest::CopyOutputRequestCallback result_callback) {
-  DCHECK(surface_info_.is_valid());
-  DCHECK(!result_callback.is_null());
+void DelegatedFrameHostAndroid::CopyFromCompositingSurface(
+    const gfx::Rect& src_subrect,
+    const gfx::Size& output_size,
+    base::OnceCallback<void(const SkBitmap&)> callback) {
+  if (!CanCopyFromCompositingSurface()) {
+    std::move(callback).Run(SkBitmap());
+    return;
+  }
 
   scoped_refptr<cc::Layer> readback_layer =
       CreateSurfaceLayer(surface_info_, !has_transparent_background_);
   readback_layer->SetHideLayerAndSubtree(true);
-  compositor->AttachLayerForReadback(readback_layer);
-  std::unique_ptr<viz::CopyOutputRequest> copy_output_request =
+  view_->GetWindowAndroid()->GetCompositor()->AttachLayerForReadback(
+      readback_layer);
+  std::unique_ptr<viz::CopyOutputRequest> request =
       std::make_unique<viz::CopyOutputRequest>(
-          viz::CopyOutputRequest::ResultFormat::RGBA_TEXTURE,
-          base::BindOnce(&CopyOutputRequestCallback, readback_layer,
-                         std::move(result_callback)));
+          viz::CopyOutputRequest::ResultFormat::RGBA_BITMAP,
+          base::BindOnce(
+              [](base::OnceCallback<void(const SkBitmap&)> callback,
+                 scoped_refptr<cc::Layer> readback_layer,
+                 std::unique_ptr<viz::CopyOutputResult> result) {
+                readback_layer->RemoveFromParent();
+                std::move(callback).Run(result->AsSkBitmap());
+              },
+              std::move(callback), std::move(readback_layer)));
 
-  if (!src_subrect_in_pixel.IsEmpty())
-    copy_output_request->set_area(src_subrect_in_pixel);
+  if (src_subrect.IsEmpty()) {
+    request->set_area(gfx::Rect(surface_info_.size_in_pixels()));
+  } else {
+    request->set_area(
+        gfx::ConvertRectToPixel(view_->GetDipScale(), src_subrect));
+  }
 
-  support_->RequestCopyOfSurface(std::move(copy_output_request));
+  if (!output_size.IsEmpty()) {
+    request->set_result_selection(gfx::Rect(output_size));
+    request->SetScaleRatio(
+        gfx::Vector2d(request->area().width(), request->area().height()),
+        gfx::Vector2d(output_size.width(), output_size.height()));
+  }
+
+  support_->RequestCopyOfSurface(std::move(request));
+}
+
+bool DelegatedFrameHostAndroid::CanCopyFromCompositingSurface() const {
+  return support_ && surface_info_.is_valid() && view_->GetWindowAndroid() &&
+         view_->GetWindowAndroid()->GetCompositor();
 }
 
 void DelegatedFrameHostAndroid::DestroyDelegatedContent() {
diff --git a/ui/android/delegated_frame_host_android.h b/ui/android/delegated_frame_host_android.h
index 4f65223..c0fbac92 100644
--- a/ui/android/delegated_frame_host_android.h
+++ b/ui/android/delegated_frame_host_android.h
@@ -69,10 +69,11 @@
   // Should only be called when the host has a content layer. Use this for one-
   // off screen capture, not for video. Always provides RGBA_BITMAP
   // CopyOutputResults.
-  void RequestCopyOfSurface(
-      WindowAndroidCompositor* compositor,
-      const gfx::Rect& src_subrect_in_pixel,
-      viz::CopyOutputRequest::CopyOutputRequestCallback result_callback);
+  void CopyFromCompositingSurface(
+      const gfx::Rect& src_subrect,
+      const gfx::Size& output_size,
+      base::OnceCallback<void(const SkBitmap&)> callback);
+  bool CanCopyFromCompositingSurface() const;
 
   void CompositorFrameSinkChanged();
 
diff --git a/ui/aura/mus/window_port_mus.cc b/ui/aura/mus/window_port_mus.cc
index aa63321..8bbd421 100644
--- a/ui/aura/mus/window_port_mus.cc
+++ b/ui/aura/mus/window_port_mus.cc
@@ -19,6 +19,7 @@
 #include "ui/aura/window_delegate.h"
 #include "ui/aura/window_observer.h"
 #include "ui/base/class_property.h"
+#include "ui/base/ui_base_features.h"
 #include "ui/base/ui_base_switches_util.h"
 #include "ui/display/display.h"
 #include "ui/display/screen.h"
@@ -407,7 +408,7 @@
 }
 
 const viz::LocalSurfaceId& WindowPortMus::GetLocalSurfaceId() {
-  if (switches::IsMusHostingViz())
+  if (base::FeatureList::IsEnabled(features::kMash))
     return local_surface_id_;
   if (!window_->IsEmbeddingClient() && !window_->IsRootWindow())
     return local_surface_id_;
@@ -560,7 +561,7 @@
   DCHECK(!local_layer_tree_frame_sink_);
 
   std::unique_ptr<cc::LayerTreeFrameSink> frame_sink;
-  if (switches::IsMusHostingViz()) {
+  if (base::FeatureList::IsEnabled(features::kMash)) {
     auto client_layer_tree_frame_sink =
         RequestLayerTreeFrameSink(nullptr, aura::Env::GetInstance()
                                                ->context_factory()
@@ -599,7 +600,7 @@
 }
 
 void WindowPortMus::OnWindowAddedToRootWindow() {
-  if (switches::IsMusHostingViz())
+  if (base::FeatureList::IsEnabled(features::kMash))
     return;
   if (local_layer_tree_frame_sink_) {
     DCHECK(!is_frame_sink_id_added_to_compositor_);
@@ -609,7 +610,7 @@
 }
 
 void WindowPortMus::OnWillRemoveWindowFromRootWindow() {
-  if (switches::IsMusHostingViz())
+  if (base::FeatureList::IsEnabled(features::kMash))
     return;
   if (is_frame_sink_id_added_to_compositor_) {
     window_->layer()->GetCompositor()->RemoveFrameSink(GetFrameSinkId());
@@ -642,7 +643,7 @@
 }
 
 void WindowPortMus::UpdateClientSurfaceEmbedder() {
-  if (!switches::IsMusHostingViz())
+  if (!base::FeatureList::IsEnabled(features::kMash))
     return;
   if (window_mus_type() != WindowMusType::TOP_LEVEL_IN_WM &&
       window_mus_type() != WindowMusType::EMBED_IN_OWNER &&
@@ -664,7 +665,7 @@
 void WindowPortMus::OnSurfaceChanged(const viz::SurfaceInfo& surface_info) {
   // TODO(fsamuel): Rename OnFirstSurfaceActivation() and set primary earlier
   // based on feedback from LayerTreeFrameSinkLocal.
-  DCHECK(!switches::IsMusHostingViz());
+  DCHECK(!base::FeatureList::IsEnabled(features::kMash));
   DCHECK_EQ(surface_info.id().frame_sink_id(), GetFrameSinkId());
   DCHECK_EQ(surface_info.id().local_surface_id(), local_surface_id_);
   window_->layer()->SetShowPrimarySurface(
diff --git a/ui/aura/mus/window_port_mus_unittest.cc b/ui/aura/mus/window_port_mus_unittest.cc
index b9fefa4..217a3f6d 100644
--- a/ui/aura/mus/window_port_mus_unittest.cc
+++ b/ui/aura/mus/window_port_mus_unittest.cc
@@ -10,7 +10,7 @@
 #include "ui/aura/mus/client_surface_embedder.h"
 #include "ui/aura/test/aura_test_base.h"
 #include "ui/aura/window.h"
-#include "ui/base/ui_base_switches_util.h"
+#include "ui/base/ui_base_features.h"
 
 namespace aura {
 
@@ -47,7 +47,7 @@
   auto mus_frame_sink = GetFrameSinkFor(&window);
   ASSERT_TRUE(mus_frame_sink);
   auto frame_sink_local_surface_id =
-      switches::IsMusHostingViz()
+      base::FeatureList::IsEnabled(features::kMash)
           ? static_cast<viz::ClientLayerTreeFrameSink*>(mus_frame_sink.get())
                 ->local_surface_id()
           : static_cast<LayerTreeFrameSinkLocal*>(mus_frame_sink.get())
@@ -58,7 +58,7 @@
 
 TEST_F(WindowPortMusTest, ClientSurfaceEmbedderUpdatesLayer) {
   // If mus is not hosting viz, we don't have ClientSurfaceEmbedder.
-  if (!switches::IsMusHostingViz())
+  if (!base::FeatureList::IsEnabled(features::kMash))
     return;
 
   Window window(nullptr);
diff --git a/ui/aura/mus/window_tree_client.cc b/ui/aura/mus/window_tree_client.cc
index c1de3da6..54665097 100644
--- a/ui/aura/mus/window_tree_client.cc
+++ b/ui/aura/mus/window_tree_client.cc
@@ -57,6 +57,7 @@
 #include "ui/aura/window_port_for_shutdown.h"
 #include "ui/aura/window_tracker.h"
 #include "ui/base/layout.h"
+#include "ui/base/ui_base_features.h"
 #include "ui/base/ui_base_switches_util.h"
 #include "ui/base/ui_base_types.h"
 #include "ui/display/screen.h"
@@ -214,7 +215,7 @@
       io_task_runner = io_thread_->task_runner();
     }
 
-    if (switches::IsMusHostingViz()) {
+    if (base::FeatureList::IsEnabled(features::kMash)) {
       gpu_ =
           ui::Gpu::Create(connector, ui::mojom::kServiceName, io_task_runner);
       compositor_context_factory_ =
@@ -595,7 +596,8 @@
   if (window_manager_delegate_ &&
       (window_mus_type == WindowMusType::EMBED ||
        window_mus_type == WindowMusType::DISPLAY_AUTOMATICALLY_CREATED)) {
-    init_params.uses_real_accelerated_widget = !::switches::IsMusHostingViz();
+    init_params.uses_real_accelerated_widget =
+        !::base::FeatureList::IsEnabled(features::kMash);
   }
   std::unique_ptr<WindowTreeHostMus> window_tree_host =
       std::make_unique<WindowTreeHostMus>(std::move(init_params));
@@ -875,8 +877,9 @@
       window_manager_client_->SetDisplayRoot(
           display, display_init_params->viewport_metrics.Clone(),
           display_init_params->is_primary_display, window->server_id(),
-          switches::IsMusHostingViz() ? display_init_params->mirrors
-                                      : std::vector<display::Display>(),
+          base::FeatureList::IsEnabled(features::kMash)
+              ? display_init_params->mirrors
+              : std::vector<display::Display>(),
           base::Bind(&OnAckMustSucceed, FROM_HERE));
     }
   }
diff --git a/ui/aura/mus/window_tree_client_unittest.cc b/ui/aura/mus/window_tree_client_unittest.cc
index 07e3853..51dc7e4 100644
--- a/ui/aura/mus/window_tree_client_unittest.cc
+++ b/ui/aura/mus/window_tree_client_unittest.cc
@@ -11,6 +11,7 @@
 #include "base/macros.h"
 #include "base/memory/ptr_util.h"
 #include "base/strings/utf_string_conversions.h"
+#include "base/test/scoped_feature_list.h"
 #include "build/build_config.h"
 #include "cc/base/switches.h"
 #include "components/viz/common/surfaces/surface_info.h"
@@ -44,6 +45,7 @@
 #include "ui/aura/window_tracker.h"
 #include "ui/aura/window_tree_host_observer.h"
 #include "ui/base/class_property.h"
+#include "ui/base/ui_base_features.h"
 #include "ui/base/ui_base_switches.h"
 #include "ui/compositor/compositor.h"
 #include "ui/display/display.h"
@@ -194,9 +196,7 @@
 
   // WindowTreeClientWmTest:
   void SetUp() override {
-    base::CommandLine::ForCurrentProcess()->AppendSwitch(switches::kMus);
-    base::CommandLine::ForCurrentProcess()->AppendSwitch(
-        switches::kMusHostingViz);
+    feature_list_.InitAndEnableFeature(features::kMash);
     if (GetParam()) {
       base::CommandLine::ForCurrentProcess()->AppendSwitchASCII(
           switches::kForceDeviceScaleFactor, "2");
@@ -205,6 +205,8 @@
   }
 
  private:
+  base::test::ScopedFeatureList feature_list_;
+
   DISALLOW_COPY_AND_ASSIGN(WindowTreeClientWmTestSurfaceSync);
 };
 
diff --git a/ui/aura/mus/window_tree_host_mus.cc b/ui/aura/mus/window_tree_host_mus.cc
index a146cfc..d3eddf7f 100644
--- a/ui/aura/mus/window_tree_host_mus.cc
+++ b/ui/aura/mus/window_tree_host_mus.cc
@@ -15,6 +15,7 @@
 #include "ui/aura/window_event_dispatcher.h"
 #include "ui/aura/window_tree_host_observer.h"
 #include "ui/base/class_property.h"
+#include "ui/base/ui_base_features.h"
 #include "ui/base/ui_base_switches_util.h"
 #include "ui/display/display.h"
 #include "ui/display/screen.h"
@@ -60,8 +61,9 @@
   // If window-server is hosting viz, then use the FrameSinkId from the server.
   // In other cases, let a valid FrameSinkId be selected by
   // context_factory_private().
-  CreateCompositor(switches::IsMusHostingViz() ? window_mus->GetFrameSinkId()
-                                               : viz::FrameSinkId());
+  CreateCompositor(base::FeatureList::IsEnabled(features::kMash)
+                       ? window_mus->GetFrameSinkId()
+                       : viz::FrameSinkId());
   if (!init_params.uses_real_accelerated_widget) {
     gfx::AcceleratedWidget accelerated_widget;
 // We need accelerated widget numbers to be different for each window and
diff --git a/ui/aura/test/aura_test_base.cc b/ui/aura/test/aura_test_base.cc
index caa8b28..ba0f3f8 100644
--- a/ui/aura/test/aura_test_base.cc
+++ b/ui/aura/test/aura_test_base.cc
@@ -6,6 +6,7 @@
 
 #include "base/command_line.h"
 #include "base/memory/ptr_util.h"
+#include "base/test/scoped_feature_list.h"
 #include "ui/aura/client/window_parenting_client.h"
 #include "ui/aura/mus/property_utils.h"
 #include "ui/aura/mus/window_tree_client.h"
@@ -16,6 +17,7 @@
 #include "ui/base/ime/input_method_initializer.h"
 #include "ui/base/material_design/material_design_controller.h"
 #include "ui/base/test/material_design_controller_test_api.h"
+#include "ui/base/ui_base_features.h"
 #include "ui/base/ui_base_switches.h"
 #include "ui/base/ui_base_switches_util.h"
 #include "ui/compositor/test/context_factories_for_test.h"
@@ -83,7 +85,7 @@
   // The ContextFactory must exist before any Compositors are created.
   ui::ContextFactory* context_factory = nullptr;
   ui::ContextFactoryPrivate* context_factory_private = nullptr;
-  if (use_mus_ && switches::IsMusHostingViz()) {
+  if (use_mus_ && base::FeatureList::IsEnabled(features::kMash)) {
     mus_context_factory_ = std::make_unique<AuraTestContextFactory>();
     context_factory = mus_context_factory_.get();
   } else {
@@ -150,10 +152,9 @@
 void AuraTestBase::ConfigureBackend(BackendType type) {
   if (type != BackendType::CLASSIC)
     EnableMusWithTestWindowTree();
-  if (type == BackendType::MUS_HOSTING_VIZ) {
-    base::CommandLine::ForCurrentProcess()->AppendSwitch(switches::kMus);
-    base::CommandLine::ForCurrentProcess()->AppendSwitch(
-        switches::kMusHostingViz);
+  if (type == BackendType::MASH) {
+    feature_list_ = std::make_unique<base::test::ScopedFeatureList>();
+    feature_list_->InitAndEnableFeature(features::kMash);
   }
 }
 
diff --git a/ui/aura/test/aura_test_base.h b/ui/aura/test/aura_test_base.h
index f412b41..3c155111 100644
--- a/ui/aura/test/aura_test_base.h
+++ b/ui/aura/test/aura_test_base.h
@@ -17,6 +17,12 @@
 #include "ui/aura/mus/window_tree_client_delegate.h"
 #include "ui/aura/test/aura_test_helper.h"
 
+namespace base {
+namespace test {
+class ScopedFeatureList;
+}
+}  // namespace base
+
 namespace ui {
 namespace mojom {
 class WindowTreeClient;
@@ -37,7 +43,7 @@
 
 class AuraTestContextFactory;
 
-enum class BackendType { CLASSIC, MUS, MUS_HOSTING_VIZ };
+enum class BackendType { CLASSIC, MUS, MASH };
 
 // A base class for aura unit tests.
 // TODO(beng): Instances of this test will create and own a RootWindow.
@@ -162,6 +168,8 @@
  private:
   base::test::ScopedTaskEnvironment scoped_task_environment_;
 
+  std::unique_ptr<base::test::ScopedFeatureList> feature_list_;
+
   // Only used for mus. Both are are initialized to this, but may be reset.
   WindowManagerDelegate* window_manager_delegate_;
   WindowTreeClientDelegate* window_tree_client_delegate_;
diff --git a/ui/aura/test/aura_test_suite_setup.cc b/ui/aura/test/aura_test_suite_setup.cc
index 8f20e537..7965032 100644
--- a/ui/aura/test/aura_test_suite_setup.cc
+++ b/ui/aura/test/aura_test_suite_setup.cc
@@ -4,10 +4,11 @@
 
 #include "ui/aura/test/aura_test_suite_setup.h"
 
+#include "base/base_switches.h"
 #include "base/command_line.h"
 #include "ui/aura/env.h"
 #include "ui/aura/test/aura_test_context_factory.h"
-#include "ui/base/ui_base_switches.h"
+#include "ui/base/ui_base_features.h"
 
 #if defined(USE_OZONE)
 #include "services/ui/public/cpp/input_devices/input_device_client.h"
@@ -40,9 +41,7 @@
   DCHECK(!Env::GetInstanceDontCreate());
 #if BUILDFLAG(ENABLE_MUS)
   const Env::Mode env_mode =
-      base::CommandLine::ForCurrentProcess()->HasSwitch(switches::kMus)
-          ? Env::Mode::MUS
-          : Env::Mode::LOCAL;
+      features::IsMusEnabled() ? Env::Mode::MUS : Env::Mode::LOCAL;
   env_ = Env::CreateInstance(env_mode);
   if (env_mode == Env::Mode::MUS)
     ConfigureMus();
diff --git a/ui/aura/window_event_dispatcher_unittest.cc b/ui/aura/window_event_dispatcher_unittest.cc
index f70d4b41..dc9571e 100644
--- a/ui/aura/window_event_dispatcher_unittest.cc
+++ b/ui/aura/window_event_dispatcher_unittest.cc
@@ -35,6 +35,7 @@
 #include "ui/aura/window_targeter.h"
 #include "ui/aura/window_tracker.h"
 #include "ui/base/hit_test.h"
+#include "ui/base/ui_base_features.h"
 #include "ui/display/screen.h"
 #include "ui/events/event.h"
 #include "ui/events/event_handler.h"
@@ -2959,19 +2960,19 @@
                         WindowEventDispatcherTest,
                         ::testing::Values(test::BackendType::CLASSIC,
                                           test::BackendType::MUS,
-                                          test::BackendType::MUS_HOSTING_VIZ));
+                                          test::BackendType::MASH));
 
 INSTANTIATE_TEST_CASE_P(/* no prefix */,
                         WindowEventDispatcherTestWithMessageLoop,
                         ::testing::Values(test::BackendType::CLASSIC,
                                           test::BackendType::MUS,
-                                          test::BackendType::MUS_HOSTING_VIZ));
+                                          test::BackendType::MASH));
 
 INSTANTIATE_TEST_CASE_P(/* no prefix */,
                         WindowEventDispatcherTestInHighDPI,
                         ::testing::Values(test::BackendType::CLASSIC,
                                           test::BackendType::MUS,
-                                          test::BackendType::MUS_HOSTING_VIZ));
+                                          test::BackendType::MASH));
 
 using WindowEventDispatcherMusTest = test::AuraTestBaseMus;
 
diff --git a/ui/aura/window_targeter_unittest.cc b/ui/aura/window_targeter_unittest.cc
index d81560a..a87f7647 100644
--- a/ui/aura/window_targeter_unittest.cc
+++ b/ui/aura/window_targeter_unittest.cc
@@ -303,6 +303,6 @@
                         WindowTargeterTest,
                         ::testing::Values(test::BackendType::CLASSIC,
                                           test::BackendType::MUS,
-                                          test::BackendType::MUS_HOSTING_VIZ));
+                                          test::BackendType::MASH));
 
 }  // namespace aura
diff --git a/ui/aura/window_unittest.cc b/ui/aura/window_unittest.cc
index ad4adf87..e1eb511 100644
--- a/ui/aura/window_unittest.cc
+++ b/ui/aura/window_unittest.cc
@@ -422,14 +422,14 @@
 // Tests that the root window gets a valid LocalSurfaceId.
 TEST_P(WindowTest, RootWindowHasValidLocalSurfaceId) {
   // When mus is hosting viz, the LocalSurfaceId is sent from mus.
-  if (GetParam() == BackendType::MUS_HOSTING_VIZ)
+  if (GetParam() == BackendType::MASH)
     return;
   EXPECT_TRUE(root_window()->GetLocalSurfaceId().is_valid());
 }
 
 TEST_P(WindowTest, WindowEmbeddingClientHasValidLocalSurfaceId) {
   // When mus is hosting viz, the LocalSurfaceId is sent from mus.
-  if (GetParam() == BackendType::MUS_HOSTING_VIZ)
+  if (GetParam() == BackendType::MASH)
     return;
   std::unique_ptr<Window> window(CreateTestWindow(
       SK_ColorWHITE, 1, gfx::Rect(10, 10, 300, 200), root_window()));
@@ -3265,13 +3265,13 @@
                         WindowTest,
                         ::testing::Values(BackendType::CLASSIC,
                                           BackendType::MUS,
-                                          BackendType::MUS_HOSTING_VIZ));
+                                          BackendType::MASH));
 
 INSTANTIATE_TEST_CASE_P(/* no prefix */,
                         WindowObserverTest,
                         ::testing::Values(BackendType::CLASSIC,
                                           BackendType::MUS,
-                                          BackendType::MUS_HOSTING_VIZ));
+                                          BackendType::MASH));
 
 }  // namespace
 }  // namespace test
diff --git a/ui/base/ui_base_features.cc b/ui/base/ui_base_features.cc
index 057eb53..3e1850d 100644
--- a/ui/base/ui_base_features.cc
+++ b/ui/base/ui_base_features.cc
@@ -52,4 +52,21 @@
 }
 #endif  // defined(OS_WIN)
 
+// Used to have ash run in its own process. This implicitly turns on the
+// WindowService. That is, if this is set IsMusEnabled() returns true.
+const base::Feature kMash = {"Mash", base::FEATURE_DISABLED_BY_DEFAULT};
+
+// Used to control the mus service (aka the UI service). This makes mus run in
+// process.
+const base::Feature kMus = {"Mus", base::FEATURE_DISABLED_BY_DEFAULT};
+
+bool IsMusEnabled() {
+#if defined(USE_AURA)
+  return base::FeatureList::IsEnabled(features::kMus) ||
+         base::FeatureList::IsEnabled(features::kMash);
+#else
+  return false;
+#endif
+}
+
 }  // namespace features
diff --git a/ui/base/ui_base_features.h b/ui/base/ui_base_features.h
index e0a2a41..acff7278 100644
--- a/ui/base/ui_base_features.h
+++ b/ui/base/ui_base_features.h
@@ -24,9 +24,20 @@
 
 // Returns true if the system should use WM_POINTER events for touch events.
 UI_BASE_EXPORT bool IsUsingWMPointerForTouch();
-
 #endif  // defined(OS_WIN)
 
+// TODO(sky): rename this to something that better conveys what it means.
+UI_BASE_EXPORT extern const base::Feature kMash;
+// WARNING: generally you should only use this in tests to enable the feature.
+// Outside of tests use IsMusEnabled() to detect if mus is enabled.
+// TODO(sky): rename this to kWindowService.
+UI_BASE_EXPORT extern const base::Feature kMus;
+
+// Returns true if mus (the Window Service) is enabled.
+// NOTE: this returns true if either kMus or kMash is specified.
+// TODO(sky): rename this to IsWindowServiceEnabled().
+UI_BASE_EXPORT bool IsMusEnabled();
+
 }  // namespace features
 
 #endif  // UI_BASE_UI_BASE_FEATURES_H_
diff --git a/ui/base/ui_base_switches.cc b/ui/base/ui_base_switches.cc
index 48139b77d..2541401 100644
--- a/ui/base/ui_base_switches.cc
+++ b/ui/base/ui_base_switches.cc
@@ -117,14 +117,4 @@
 // Tint GL-composited color.
 const char kTintGlCompositedContent[] = "tint-gl-composited-content";
 
-#if defined(USE_AURA)
-// Used to enable the mus service (aka the UI service). This makes mus run in
-// process. It is also used to notify the clients that the UI service is being
-// used.
-const char kMus[] = "mus";
-
-// If set mus is hosting Viz. Only applicable is kMus if specified.
-const char kMusHostingViz[] = "mus-hosting-viz";
-#endif
-
 }  // namespace switches
diff --git a/ui/base/ui_base_switches.h b/ui/base/ui_base_switches.h
index e874680..2ce5803 100644
--- a/ui/base/ui_base_switches.h
+++ b/ui/base/ui_base_switches.h
@@ -46,11 +46,6 @@
 UI_BASE_EXPORT extern const char kDisallowNonExactResourceReuse[];
 UI_BASE_EXPORT extern const char kMangleLocalizedStrings[];
 
-#if defined(USE_AURA)
-UI_BASE_EXPORT extern const char kMus[];
-UI_BASE_EXPORT extern const char kMusHostingViz[];
-#endif
-
 }  // namespace switches
 
 #endif  // UI_BASE_UI_BASE_SWITCHES_H_
diff --git a/ui/base/ui_base_switches_util.cc b/ui/base/ui_base_switches_util.cc
index e5d7351e..ad8161db 100644
--- a/ui/base/ui_base_switches_util.cc
+++ b/ui/base/ui_base_switches_util.cc
@@ -13,20 +13,10 @@
 bool IsTouchDragDropEnabled() {
 #if defined(OS_CHROMEOS) || defined(OS_ANDROID)
   return !base::CommandLine::ForCurrentProcess()->HasSwitch(
-      switches::kDisableTouchDragDrop);
+      kDisableTouchDragDrop);
 #else
   return base::CommandLine::ForCurrentProcess()->HasSwitch(
-      switches::kEnableTouchDragDrop);
-#endif
-}
-
-bool IsMusHostingViz() {
-#if defined(USE_AURA)
-  auto* cmd = base::CommandLine::ForCurrentProcess();
-  return cmd->HasSwitch(switches::kMus) &&
-         cmd->HasSwitch(switches::kMusHostingViz);
-#else
-  return false;
+      kEnableTouchDragDrop);
 #endif
 }
 
diff --git a/ui/base/ui_base_switches_util.h b/ui/base/ui_base_switches_util.h
index fc2f007..f84ed94 100644
--- a/ui/base/ui_base_switches_util.h
+++ b/ui/base/ui_base_switches_util.h
@@ -12,10 +12,6 @@
 UI_BASE_EXPORT bool IsLinkDisambiguationPopupEnabled();
 UI_BASE_EXPORT bool IsTouchDragDropEnabled();
 
-// Returns whether mus is hosting viz. Mus is hosting viz only if
-// --mus-hosting-viz is set.
-UI_BASE_EXPORT bool IsMusHostingViz();
-
 // Returns whether the touchable app context menu switch has been set. Prefer
 // features::IsTouchableAppContextMenuEnabled().
 UI_BASE_EXPORT bool IsTouchableAppContextMenuEnabled();
diff --git a/ui/base/ui_features.gni b/ui/base/ui_features.gni
index 708a8f8..806ef95 100644
--- a/ui/base/ui_features.gni
+++ b/ui/base/ui_features.gni
@@ -17,8 +17,8 @@
   # Whether the message center should be included for displaying notifications.
   enable_message_center = is_win || is_mac || is_linux || is_chromeos
 
-  # Set to true to if mus (aka the UI service) is enabled. Use --mus (or --mash
-  # in chrome code) to start in mus/mash.
+  # Set to true to if mus (aka the UI service) is enabled. Use the features kMus
+  # (or kMash in chrome code) to start in mus/mash.
   enable_mus = is_chromeos
 
   # Optimize parts of Chrome's UI written with web technologies (HTML/CSS/JS)
diff --git a/ui/events/android/motion_event_android.cc b/ui/events/android/motion_event_android.cc
index 8021d0a..a6ad292 100644
--- a/ui/events/android/motion_event_android.cc
+++ b/ui/events/android/motion_event_android.cc
@@ -20,43 +20,91 @@
 namespace ui {
 namespace {
 
-#define EVENT_CASE(x)      \
-  case JNI_MotionEvent::x: \
-    return MotionEventAndroid::x
+#define ACTION_CASE(x)              \
+  case JNI_MotionEvent::ACTION_##x: \
+    return MotionEventAndroid::Action::x
+
+#define ACTION_REVERSE_CASE(x)        \
+  case MotionEventAndroid::Action::x: \
+    return JNI_MotionEvent::ACTION_##x
+
+#define TOOL_TYPE_CASE(x)              \
+  case JNI_MotionEvent::TOOL_TYPE_##x: \
+    return MotionEventAndroid::ToolType::x
+
+#define TOOL_TYPE_REVERSE_CASE(x)       \
+  case MotionEventAndroid::ToolType::x: \
+    return JNI_MotionEvent::TOOL_TYPE_##x
 
 MotionEventAndroid::Action FromAndroidAction(int android_action) {
   switch (android_action) {
-    EVENT_CASE(ACTION_DOWN);
-    EVENT_CASE(ACTION_UP);
-    EVENT_CASE(ACTION_MOVE);
-    EVENT_CASE(ACTION_CANCEL);
-    EVENT_CASE(ACTION_POINTER_DOWN);
-    EVENT_CASE(ACTION_POINTER_UP);
-    EVENT_CASE(ACTION_HOVER_ENTER);
-    EVENT_CASE(ACTION_HOVER_EXIT);
-    EVENT_CASE(ACTION_HOVER_MOVE);
-    EVENT_CASE(ACTION_BUTTON_PRESS);
-    EVENT_CASE(ACTION_BUTTON_RELEASE);
+    ACTION_CASE(DOWN);
+    ACTION_CASE(UP);
+    ACTION_CASE(MOVE);
+    ACTION_CASE(CANCEL);
+    ACTION_CASE(POINTER_DOWN);
+    ACTION_CASE(POINTER_UP);
+    ACTION_CASE(HOVER_ENTER);
+    ACTION_CASE(HOVER_EXIT);
+    ACTION_CASE(HOVER_MOVE);
+    ACTION_CASE(BUTTON_PRESS);
+    ACTION_CASE(BUTTON_RELEASE);
     default:
       NOTREACHED() << "Invalid Android MotionEvent action: " << android_action;
   };
-  return MotionEventAndroid::ACTION_CANCEL;
+  return MotionEventAndroid::Action::CANCEL;
+}
+
+int ToAndroidAction(MotionEventAndroid::Action action) {
+  switch (action) {
+    ACTION_REVERSE_CASE(DOWN);
+    ACTION_REVERSE_CASE(UP);
+    ACTION_REVERSE_CASE(MOVE);
+    ACTION_REVERSE_CASE(CANCEL);
+    ACTION_REVERSE_CASE(POINTER_DOWN);
+    ACTION_REVERSE_CASE(POINTER_UP);
+    ACTION_REVERSE_CASE(HOVER_ENTER);
+    ACTION_REVERSE_CASE(HOVER_EXIT);
+    ACTION_REVERSE_CASE(HOVER_MOVE);
+    ACTION_REVERSE_CASE(BUTTON_PRESS);
+    ACTION_REVERSE_CASE(BUTTON_RELEASE);
+    default:
+      NOTREACHED() << "Invalid MotionEvent action: " << action;
+  };
+  return JNI_MotionEvent::ACTION_CANCEL;
 }
 
 MotionEventAndroid::ToolType FromAndroidToolType(int android_tool_type) {
   switch (android_tool_type) {
-    EVENT_CASE(TOOL_TYPE_UNKNOWN);
-    EVENT_CASE(TOOL_TYPE_FINGER);
-    EVENT_CASE(TOOL_TYPE_STYLUS);
-    EVENT_CASE(TOOL_TYPE_MOUSE);
-    EVENT_CASE(TOOL_TYPE_ERASER);
+    TOOL_TYPE_CASE(UNKNOWN);
+    TOOL_TYPE_CASE(FINGER);
+    TOOL_TYPE_CASE(STYLUS);
+    TOOL_TYPE_CASE(MOUSE);
+    TOOL_TYPE_CASE(ERASER);
     default:
       NOTREACHED() << "Invalid Android MotionEvent tool type: "
                    << android_tool_type;
   };
-  return MotionEventAndroid::TOOL_TYPE_UNKNOWN;
+  return MotionEventAndroid::ToolType::UNKNOWN;
 }
-#undef EVENT_CASE
+
+int ToAndroidToolType(MotionEventAndroid::ToolType tool_type) {
+  switch (tool_type) {
+    TOOL_TYPE_REVERSE_CASE(UNKNOWN);
+    TOOL_TYPE_REVERSE_CASE(FINGER);
+    TOOL_TYPE_REVERSE_CASE(STYLUS);
+    TOOL_TYPE_REVERSE_CASE(MOUSE);
+    TOOL_TYPE_REVERSE_CASE(ERASER);
+    default:
+      NOTREACHED() << "Invalid MotionEvent tool type: " << tool_type;
+  };
+  return JNI_MotionEvent::TOOL_TYPE_UNKNOWN;
+}
+
+#undef ACTION_CASE
+#undef ACTION_REVERSE_CASE
+#undef TOOL_TYPE_CASE
+#undef TOOL_TYPE_REVERSE_CASE
 
 int FromAndroidButtonState(int button_state) {
   int result = 0;
@@ -131,11 +179,11 @@
 
 size_t ToValidHistorySize(jint history_size, ui::MotionEvent::Action action) {
   DCHECK_GE(history_size, 0);
-  // While the spec states that only ACTION_MOVE events should contain
+  // While the spec states that only Action::MOVE events should contain
   // historical entries, it's possible that an embedder could repurpose an
-  // ACTION_MOVE event into a different kind of event. In that case, the
+  // Action::MOVE event into a different kind of event. In that case, the
   // historical values are meaningless, and should not be exposed.
-  if (action != ui::MotionEvent::ACTION_MOVE)
+  if (action != ui::MotionEvent::Action::MOVE)
     return 0;
   return history_size;
 }
@@ -179,7 +227,7 @@
       orientation(0),
       tilt_x(0),
       tilt_y(0),
-      tool_type(TOOL_TYPE_UNKNOWN) {}
+      tool_type(ToolType::UNKNOWN) {}
 
 MotionEventAndroid::MotionEventAndroid(JNIEnv* env,
                                        jobject event,
@@ -253,19 +301,12 @@
 }
 
 //  static
-int MotionEventAndroid::GetAndroidActionForTesting(int action) {
-  int android_action = JNI_MotionEvent::ACTION_CANCEL;
-  switch (action) {
-    case ui::MotionEvent::ACTION_DOWN:
-      android_action = JNI_MotionEvent::ACTION_DOWN;
-      break;
-    case ui::MotionEvent::ACTION_UP:
-      android_action = JNI_MotionEvent::ACTION_UP;
-      break;
-    default:
-      NOTIMPLEMENTED() << "Conversion not supported: " << action;
-  }
-  return android_action;
+int MotionEventAndroid::GetAndroidAction(Action action) {
+  return ToAndroidAction(action);
+}
+
+int MotionEventAndroid::GetAndroidToolType(ToolType tool_type) {
+  return ToAndroidToolType(tool_type);
 }
 
 std::unique_ptr<MotionEventAndroid> MotionEventAndroid::CreateFor(
@@ -305,8 +346,8 @@
 }
 
 int MotionEventAndroid::GetActionIndex() const {
-  DCHECK(cached_action_ == MotionEvent::ACTION_POINTER_UP ||
-         cached_action_ == MotionEvent::ACTION_POINTER_DOWN)
+  DCHECK(cached_action_ == MotionEvent::Action::POINTER_UP ||
+         cached_action_ == MotionEvent::Action::POINTER_DOWN)
       << "Invalid action for GetActionIndex(): " << cached_action_;
   DCHECK_GE(cached_action_index_, 0);
   DCHECK_LT(cached_action_index_, static_cast<int>(cached_pointer_count_));
@@ -396,7 +437,7 @@
   // accessed at most once per event instance).
   if (!event_.obj())
     return 0.f;
-  if (cached_action_ == MotionEvent::ACTION_UP)
+  if (cached_action_ == MotionEvent::Action::UP)
     return 0.f;
   return JNI_MotionEvent::Java_MotionEvent_getPressureF_I(
       AttachCurrentThread(), event_, pointer_index);
diff --git a/ui/events/android/motion_event_android.h b/ui/events/android/motion_event_android.h
index e45ece7f..53165164 100644
--- a/ui/events/android/motion_event_android.h
+++ b/ui/events/android/motion_event_android.h
@@ -27,7 +27,8 @@
  public:
   // Returns the motion event action defined in Java layer for a given
   // MotionEvent::Action.
-  static int GetAndroidActionForTesting(int action);
+  static int GetAndroidAction(Action action);
+  static int GetAndroidToolType(ToolType tool_type);
 
   struct Pointer {
     Pointer(jint id,
diff --git a/ui/events/android/motion_event_android_unittest.cc b/ui/events/android/motion_event_android_unittest.cc
index 6ca7bb7..2a6b167 100644
--- a/ui/events/android/motion_event_android_unittest.cc
+++ b/ui/events/android/motion_event_android_unittest.cc
@@ -72,7 +72,7 @@
       action_index, kAndroidActionButton, kAndroidButtonPrimary,
       kAndroidAltKeyDown, raw_offset, -raw_offset, false, &p0, &p1);
 
-  EXPECT_EQ(MotionEvent::ACTION_DOWN, event.GetAction());
+  EXPECT_EQ(MotionEvent::Action::DOWN, event.GetAction());
   EXPECT_EQ(event_time, event.GetEventTime());
   EXPECT_EQ(p0.pos_x_pixels * kPixToDip, event.GetX(0));
   EXPECT_EQ(p0.pos_y_pixels * kPixToDip, event.GetY(0));
@@ -96,8 +96,8 @@
               float_error);
   EXPECT_EQ(p0.id, event.GetPointerId(0));
   EXPECT_EQ(p1.id, event.GetPointerId(1));
-  EXPECT_EQ(MotionEvent::TOOL_TYPE_FINGER, event.GetToolType(0));
-  EXPECT_EQ(MotionEvent::TOOL_TYPE_FINGER, event.GetToolType(1));
+  EXPECT_EQ(MotionEvent::ToolType::FINGER, event.GetToolType(0));
+  EXPECT_EQ(MotionEvent::ToolType::FINGER, event.GetToolType(1));
   EXPECT_EQ(MotionEvent::BUTTON_PRIMARY, event.GetButtonState());
   EXPECT_EQ(ui::EF_ALT_DOWN | ui::EF_LEFT_MOUSE_BUTTON, event.GetFlags());
   EXPECT_EQ(static_cast<size_t>(pointer_count), event.GetPointerCount());
@@ -136,7 +136,7 @@
                            0, false, &p0, nullptr);
 
   std::unique_ptr<MotionEvent> cancel_event = event.Cancel();
-  EXPECT_EQ(MotionEvent::ACTION_CANCEL, cancel_event->GetAction());
+  EXPECT_EQ(MotionEvent::Action::CANCEL, cancel_event->GetAction());
   EXPECT_EQ(event_time, cancel_event->GetEventTime());
   EXPECT_EQ(p0.pos_x_pixels * kPixToDip, cancel_event->GetX(0));
   EXPECT_EQ(p0.pos_y_pixels * kPixToDip, cancel_event->GetY(0));
@@ -193,7 +193,7 @@
                            pointer_count, history_size, action_index, 0, 0, 0,
                            0, 0, false, &p0, &p1);
 
-  EXPECT_EQ(MotionEvent::ACTION_POINTER_DOWN, event.GetAction());
+  EXPECT_EQ(MotionEvent::Action::POINTER_DOWN, event.GetAction());
   EXPECT_EQ(action_index, event.GetActionIndex());
 }
 
diff --git a/ui/events/blink/blink_event_util.cc b/ui/events/blink/blink_event_util.cc
index 4ad488e..23b91d93 100644
--- a/ui/events/blink/blink_event_util.cc
+++ b/ui/events/blink/blink_event_util.cc
@@ -44,24 +44,24 @@
 
 WebInputEvent::Type ToWebTouchEventType(MotionEvent::Action action) {
   switch (action) {
-    case MotionEvent::ACTION_DOWN:
+    case MotionEvent::Action::DOWN:
       return WebInputEvent::kTouchStart;
-    case MotionEvent::ACTION_MOVE:
+    case MotionEvent::Action::MOVE:
       return WebInputEvent::kTouchMove;
-    case MotionEvent::ACTION_UP:
+    case MotionEvent::Action::UP:
       return WebInputEvent::kTouchEnd;
-    case MotionEvent::ACTION_CANCEL:
+    case MotionEvent::Action::CANCEL:
       return WebInputEvent::kTouchCancel;
-    case MotionEvent::ACTION_POINTER_DOWN:
+    case MotionEvent::Action::POINTER_DOWN:
       return WebInputEvent::kTouchStart;
-    case MotionEvent::ACTION_POINTER_UP:
+    case MotionEvent::Action::POINTER_UP:
       return WebInputEvent::kTouchEnd;
-    case MotionEvent::ACTION_NONE:
-    case MotionEvent::ACTION_HOVER_ENTER:
-    case MotionEvent::ACTION_HOVER_EXIT:
-    case MotionEvent::ACTION_HOVER_MOVE:
-    case MotionEvent::ACTION_BUTTON_PRESS:
-    case MotionEvent::ACTION_BUTTON_RELEASE:
+    case MotionEvent::Action::NONE:
+    case MotionEvent::Action::HOVER_ENTER:
+    case MotionEvent::Action::HOVER_EXIT:
+    case MotionEvent::Action::HOVER_MOVE:
+    case MotionEvent::Action::BUTTON_PRESS:
+    case MotionEvent::Action::BUTTON_RELEASE:
       break;
   }
   NOTREACHED() << "Invalid MotionEvent::Action = " << action;
@@ -69,50 +69,51 @@
 }
 
 // Note that the action index is meaningful only in the context of
-// |ACTION_POINTER_UP| and |ACTION_POINTER_DOWN|; other actions map directly to
-// WebTouchPoint::State.
+// |Action::POINTER_UP| and |Action::POINTER_DOWN|; other actions map directly
+// to WebTouchPoint::State.
 WebTouchPoint::State ToWebTouchPointState(const MotionEvent& event,
                                           size_t pointer_index) {
   switch (event.GetAction()) {
-    case MotionEvent::ACTION_DOWN:
+    case MotionEvent::Action::DOWN:
       return WebTouchPoint::kStatePressed;
-    case MotionEvent::ACTION_MOVE:
+    case MotionEvent::Action::MOVE:
       return WebTouchPoint::kStateMoved;
-    case MotionEvent::ACTION_UP:
+    case MotionEvent::Action::UP:
       return WebTouchPoint::kStateReleased;
-    case MotionEvent::ACTION_CANCEL:
+    case MotionEvent::Action::CANCEL:
       return WebTouchPoint::kStateCancelled;
-    case MotionEvent::ACTION_POINTER_DOWN:
+    case MotionEvent::Action::POINTER_DOWN:
       return static_cast<int>(pointer_index) == event.GetActionIndex()
                  ? WebTouchPoint::kStatePressed
                  : WebTouchPoint::kStateStationary;
-    case MotionEvent::ACTION_POINTER_UP:
+    case MotionEvent::Action::POINTER_UP:
       return static_cast<int>(pointer_index) == event.GetActionIndex()
                  ? WebTouchPoint::kStateReleased
                  : WebTouchPoint::kStateStationary;
-    case MotionEvent::ACTION_NONE:
-    case MotionEvent::ACTION_HOVER_ENTER:
-    case MotionEvent::ACTION_HOVER_EXIT:
-    case MotionEvent::ACTION_HOVER_MOVE:
-    case MotionEvent::ACTION_BUTTON_PRESS:
-    case MotionEvent::ACTION_BUTTON_RELEASE:
+    case MotionEvent::Action::NONE:
+    case MotionEvent::Action::HOVER_ENTER:
+    case MotionEvent::Action::HOVER_EXIT:
+    case MotionEvent::Action::HOVER_MOVE:
+    case MotionEvent::Action::BUTTON_PRESS:
+    case MotionEvent::Action::BUTTON_RELEASE:
       break;
   }
   NOTREACHED() << "Invalid MotionEvent::Action.";
   return WebTouchPoint::kStateUndefined;
 }
 
-WebPointerProperties::PointerType ToWebPointerType(int tool_type) {
-  switch (static_cast<MotionEvent::ToolType>(tool_type)) {
-    case MotionEvent::TOOL_TYPE_UNKNOWN:
+WebPointerProperties::PointerType ToWebPointerType(
+    MotionEvent::ToolType tool_type) {
+  switch (tool_type) {
+    case MotionEvent::ToolType::UNKNOWN:
       return WebPointerProperties::PointerType::kUnknown;
-    case MotionEvent::TOOL_TYPE_FINGER:
+    case MotionEvent::ToolType::FINGER:
       return WebPointerProperties::PointerType::kTouch;
-    case MotionEvent::TOOL_TYPE_STYLUS:
+    case MotionEvent::ToolType::STYLUS:
       return WebPointerProperties::PointerType::kPen;
-    case MotionEvent::TOOL_TYPE_MOUSE:
+    case MotionEvent::ToolType::MOUSE:
       return WebPointerProperties::PointerType::kMouse;
-    case MotionEvent::TOOL_TYPE_ERASER:
+    case MotionEvent::ToolType::ERASER:
       return WebPointerProperties::PointerType::kEraser;
   }
   NOTREACHED() << "Invalid MotionEvent::ToolType = " << tool_type;
@@ -940,23 +941,23 @@
 
 WebInputEvent::Type ToWebMouseEventType(MotionEvent::Action action) {
   switch (action) {
-    case MotionEvent::ACTION_DOWN:
-    case MotionEvent::ACTION_BUTTON_PRESS:
+    case MotionEvent::Action::DOWN:
+    case MotionEvent::Action::BUTTON_PRESS:
       return WebInputEvent::kMouseDown;
-    case MotionEvent::ACTION_MOVE:
-    case MotionEvent::ACTION_HOVER_MOVE:
+    case MotionEvent::Action::MOVE:
+    case MotionEvent::Action::HOVER_MOVE:
       return WebInputEvent::kMouseMove;
-    case MotionEvent::ACTION_HOVER_ENTER:
+    case MotionEvent::Action::HOVER_ENTER:
       return WebInputEvent::kMouseEnter;
-    case MotionEvent::ACTION_HOVER_EXIT:
+    case MotionEvent::Action::HOVER_EXIT:
       return WebInputEvent::kMouseLeave;
-    case MotionEvent::ACTION_UP:
-    case MotionEvent::ACTION_BUTTON_RELEASE:
+    case MotionEvent::Action::UP:
+    case MotionEvent::Action::BUTTON_RELEASE:
       return WebInputEvent::kMouseUp;
-    case MotionEvent::ACTION_NONE:
-    case MotionEvent::ACTION_CANCEL:
-    case MotionEvent::ACTION_POINTER_DOWN:
-    case MotionEvent::ACTION_POINTER_UP:
+    case MotionEvent::Action::NONE:
+    case MotionEvent::Action::CANCEL:
+    case MotionEvent::Action::POINTER_DOWN:
+    case MotionEvent::Action::POINTER_UP:
       break;
   }
   NOTREACHED() << "Invalid MotionEvent::Action = " << action;
@@ -1051,11 +1052,11 @@
     float tilt_x,
     float tilt_y,
     int android_buttons_changed,
-    int tool_type) {
+    MotionEvent::ToolType tool_type) {
   webPointerProperties.id = pointer_id;
   webPointerProperties.force = pressure;
 
-  if (tool_type == MotionEvent::TOOL_TYPE_STYLUS) {
+  if (tool_type == MotionEvent::ToolType::STYLUS) {
     // A stylus points to a direction specified by orientation and tilts to
     // the opposite direction. Coordinate system is left-handed.
     webPointerProperties.tilt_x = tilt_x;
diff --git a/ui/events/blink/blink_event_util.h b/ui/events/blink/blink_event_util.h
index 2f9e512..24b85de 100644
--- a/ui/events/blink/blink_event_util.h
+++ b/ui/events/blink/blink_event_util.h
@@ -86,7 +86,7 @@
     float tilt_x,
     float tilt_y,
     int android_buttons_changed,
-    int tool_type);
+    MotionEvent::ToolType tool_type);
 
 int WebEventModifiersToEventFlags(int modifiers);
 
diff --git a/ui/events/gesture_detection/filtered_gesture_provider.cc b/ui/events/gesture_detection/filtered_gesture_provider.cc
index 10b93b2..8038aa73 100644
--- a/ui/events/gesture_detection/filtered_gesture_provider.cc
+++ b/ui/events/gesture_detection/filtered_gesture_provider.cc
@@ -31,7 +31,7 @@
 
   pending_gesture_packet_ = GestureEventDataPacket::FromTouch(event);
 
-  if (event.GetAction() == MotionEvent::ACTION_DOWN)
+  if (event.GetAction() == MotionEvent::Action::DOWN)
     any_touch_moved_beyond_slop_region_ = false;
 
   if (!gesture_provider_.OnTouchEvent(event))
diff --git a/ui/events/gesture_detection/gesture_detector.cc b/ui/events/gesture_detection/gesture_detector.cc
index adb22f5..baa63d3 100644
--- a/ui/events/gesture_detection/gesture_detector.cc
+++ b/ui/events/gesture_detection/gesture_detector.cc
@@ -150,7 +150,7 @@
 
   velocity_tracker_.AddMovement(ev);
 
-  const bool pointer_up = action == MotionEvent::ACTION_POINTER_UP;
+  const bool pointer_up = action == MotionEvent::Action::POINTER_UP;
   const int skip_index = pointer_up ? ev.GetActionIndex() : -1;
 
   // Determine focal point.
@@ -169,16 +169,16 @@
   bool handled = false;
 
   switch (action) {
-    case MotionEvent::ACTION_NONE:
-    case MotionEvent::ACTION_HOVER_ENTER:
-    case MotionEvent::ACTION_HOVER_EXIT:
-    case MotionEvent::ACTION_HOVER_MOVE:
-    case MotionEvent::ACTION_BUTTON_PRESS:
-    case MotionEvent::ACTION_BUTTON_RELEASE:
+    case MotionEvent::Action::NONE:
+    case MotionEvent::Action::HOVER_ENTER:
+    case MotionEvent::Action::HOVER_EXIT:
+    case MotionEvent::Action::HOVER_MOVE:
+    case MotionEvent::Action::BUTTON_PRESS:
+    case MotionEvent::Action::BUTTON_RELEASE:
       NOTREACHED();
       return handled;
 
-    case MotionEvent::ACTION_POINTER_DOWN: {
+    case MotionEvent::Action::POINTER_DOWN: {
       down_focus_x_ = last_focus_x_ = focus_x;
       down_focus_y_ = last_focus_y_ = focus_y;
       // Cancel long press and taps.
@@ -204,7 +204,7 @@
         two_finger_tap_allowed_for_gesture_ = false;
     } break;
 
-    case MotionEvent::ACTION_POINTER_UP: {
+    case MotionEvent::Action::POINTER_UP: {
       down_focus_x_ = last_focus_x_ = focus_x;
       down_focus_y_ = last_focus_y_ = focus_y;
 
@@ -246,7 +246,7 @@
       two_finger_tap_allowed_for_gesture_ = false;
     } break;
 
-    case MotionEvent::ACTION_DOWN: {
+    case MotionEvent::Action::DOWN: {
       bool is_repeated_tap =
           current_down_event_ && previous_up_event_ &&
           IsRepeatedTap(*current_down_event_, *previous_up_event_, ev);
@@ -292,35 +292,15 @@
       handled |= listener_->OnDown(ev);
     } break;
 
-    case MotionEvent::ACTION_MOVE:
-      {
-        const float scroll_x = last_focus_x_ - focus_x;
-        const float scroll_y = last_focus_y_ - focus_y;
-        if (is_double_tapping_) {
-          // Give the move events of the double-tap.
-          DCHECK(double_tap_listener_);
-          handled |= double_tap_listener_->OnDoubleTapEvent(ev);
-        } else if (all_pointers_within_slop_regions_) {
-          if (!IsWithinTouchSlop(ev)) {
-            handled = listener_->OnScroll(
-                *current_down_event_, ev,
-                (maximum_pointer_count_ > 1 && secondary_pointer_down_event_)
-                    ? *secondary_pointer_down_event_
-                    : ev,
-                scroll_x, scroll_y);
-            last_focus_x_ = focus_x;
-            last_focus_y_ = focus_y;
-            all_pointers_within_slop_regions_ = false;
-            timeout_handler_->Stop();
-          }
-
-          const float delta_x = focus_x - down_focus_x_;
-          const float delta_y = focus_y - down_focus_y_;
-          const float distance_square = delta_x * delta_x + delta_y * delta_y;
-          if (distance_square > double_tap_touch_slop_square_)
-            always_in_bigger_tap_region_ = false;
-        } else if (std::abs(scroll_x) > kScrollEpsilon ||
-                   std::abs(scroll_y) > kScrollEpsilon) {
+    case MotionEvent::Action::MOVE: {
+      const float scroll_x = last_focus_x_ - focus_x;
+      const float scroll_y = last_focus_y_ - focus_y;
+      if (is_double_tapping_) {
+        // Give the move events of the double-tap.
+        DCHECK(double_tap_listener_);
+        handled |= double_tap_listener_->OnDoubleTapEvent(ev);
+      } else if (all_pointers_within_slop_regions_) {
+        if (!IsWithinTouchSlop(ev)) {
           handled = listener_->OnScroll(
               *current_down_event_, ev,
               (maximum_pointer_count_ > 1 && secondary_pointer_down_event_)
@@ -329,22 +309,40 @@
               scroll_x, scroll_y);
           last_focus_x_ = focus_x;
           last_focus_y_ = focus_y;
+          all_pointers_within_slop_regions_ = false;
+          timeout_handler_->Stop();
         }
 
-        if (!two_finger_tap_allowed_for_gesture_)
-          break;
-
-        // Two-finger tap should be prevented if either pointer exceeds its
-        // (independent) slop region.
-        // If the event has had more than two pointers down at any time,
-        // two finger tap should be prevented.
-        if (maximum_pointer_count_ > 2 || !IsWithinTouchSlop(ev)) {
-          two_finger_tap_allowed_for_gesture_ = false;
-        }
+        const float delta_x = focus_x - down_focus_x_;
+        const float delta_y = focus_y - down_focus_y_;
+        const float distance_square = delta_x * delta_x + delta_y * delta_y;
+        if (distance_square > double_tap_touch_slop_square_)
+          always_in_bigger_tap_region_ = false;
+      } else if (std::abs(scroll_x) > kScrollEpsilon ||
+                 std::abs(scroll_y) > kScrollEpsilon) {
+        handled = listener_->OnScroll(
+            *current_down_event_, ev,
+            (maximum_pointer_count_ > 1 && secondary_pointer_down_event_)
+                ? *secondary_pointer_down_event_
+                : ev,
+            scroll_x, scroll_y);
+        last_focus_x_ = focus_x;
+        last_focus_y_ = focus_y;
       }
-      break;
 
-    case MotionEvent::ACTION_UP:
+      if (!two_finger_tap_allowed_for_gesture_)
+        break;
+
+      // Two-finger tap should be prevented if either pointer exceeds its
+      // (independent) slop region.
+      // If the event has had more than two pointers down at any time,
+      // two finger tap should be prevented.
+      if (maximum_pointer_count_ > 2 || !IsWithinTouchSlop(ev)) {
+        two_finger_tap_allowed_for_gesture_ = false;
+      }
+    } break;
+
+    case MotionEvent::Action::UP:
       still_down_ = false;
       {
         if (is_double_tapping_) {
@@ -375,8 +373,8 @@
 
           if ((std::abs(velocity_y) > min_fling_velocity_) ||
               (std::abs(velocity_x) > min_fling_velocity_)) {
-            handled = listener_->OnFling(
-                *current_down_event_, ev, velocity_x, velocity_y);
+            handled = listener_->OnFling(*current_down_event_, ev, velocity_x,
+                                         velocity_y);
           }
 
           handled |= HandleSwipeIfNeeded(ev, velocity_x, velocity_y);
@@ -393,7 +391,7 @@
       maximum_pointer_count_ = 0;
       break;
 
-    case MotionEvent::ACTION_CANCEL:
+    case MotionEvent::Action::CANCEL:
       Cancel();
       break;
   }
diff --git a/ui/events/gesture_detection/gesture_event_data.cc b/ui/events/gesture_detection/gesture_event_data.cc
index 74a11be..e72d2104 100644
--- a/ui/events/gesture_detection/gesture_event_data.cc
+++ b/ui/events/gesture_detection/gesture_event_data.cc
@@ -12,15 +12,15 @@
 
 EventPointerType ToEventPointerType(MotionEvent::ToolType tool_type) {
   switch (tool_type) {
-    case MotionEvent::TOOL_TYPE_UNKNOWN:
+    case MotionEvent::ToolType::UNKNOWN:
       return EventPointerType::POINTER_TYPE_UNKNOWN;
-    case MotionEvent::TOOL_TYPE_FINGER:
+    case MotionEvent::ToolType::FINGER:
       return EventPointerType::POINTER_TYPE_TOUCH;
-    case MotionEvent::TOOL_TYPE_STYLUS:
+    case MotionEvent::ToolType::STYLUS:
       return EventPointerType::POINTER_TYPE_PEN;
-    case MotionEvent::TOOL_TYPE_MOUSE:
+    case MotionEvent::ToolType::MOUSE:
       return EventPointerType::POINTER_TYPE_MOUSE;
-    case MotionEvent::TOOL_TYPE_ERASER:
+    case MotionEvent::ToolType::ERASER:
       return EventPointerType::POINTER_TYPE_ERASER;
     default:
       NOTREACHED() << "Invalid ToolType = " << tool_type;
@@ -80,13 +80,12 @@
 
 GestureEventData::GestureEventData()
     : motion_event_id(0),
-      primary_tool_type(MotionEvent::TOOL_TYPE_UNKNOWN),
+      primary_tool_type(MotionEvent::ToolType::UNKNOWN),
       x(0),
       y(0),
       raw_x(0),
       raw_y(0),
       flags(EF_NONE),
-      unique_touch_event_id(0U) {
-}
+      unique_touch_event_id(0U) {}
 
 }  //  namespace ui
diff --git a/ui/events/gesture_detection/gesture_event_data_packet.cc b/ui/events/gesture_detection/gesture_event_data_packet.cc
index 5edd34a..d5b1722 100644
--- a/ui/events/gesture_detection/gesture_event_data_packet.cc
+++ b/ui/events/gesture_detection/gesture_event_data_packet.cc
@@ -13,24 +13,24 @@
 GestureEventDataPacket::GestureSource ToGestureSource(
     const ui::MotionEvent& event) {
   switch (event.GetAction()) {
-    case ui::MotionEvent::ACTION_DOWN:
+    case ui::MotionEvent::Action::DOWN:
       return GestureEventDataPacket::TOUCH_SEQUENCE_START;
-    case ui::MotionEvent::ACTION_UP:
+    case ui::MotionEvent::Action::UP:
       return GestureEventDataPacket::TOUCH_SEQUENCE_END;
-    case ui::MotionEvent::ACTION_MOVE:
+    case ui::MotionEvent::Action::MOVE:
       return GestureEventDataPacket::TOUCH_MOVE;
-    case ui::MotionEvent::ACTION_CANCEL:
+    case ui::MotionEvent::Action::CANCEL:
       return GestureEventDataPacket::TOUCH_SEQUENCE_CANCEL;
-    case ui::MotionEvent::ACTION_POINTER_DOWN:
+    case ui::MotionEvent::Action::POINTER_DOWN:
       return GestureEventDataPacket::TOUCH_START;
-    case ui::MotionEvent::ACTION_POINTER_UP:
+    case ui::MotionEvent::Action::POINTER_UP:
       return GestureEventDataPacket::TOUCH_END;
-    case ui::MotionEvent::ACTION_NONE:
-    case ui::MotionEvent::ACTION_HOVER_ENTER:
-    case ui::MotionEvent::ACTION_HOVER_EXIT:
-    case ui::MotionEvent::ACTION_HOVER_MOVE:
-    case ui::MotionEvent::ACTION_BUTTON_PRESS:
-    case ui::MotionEvent::ACTION_BUTTON_RELEASE:
+    case ui::MotionEvent::Action::NONE:
+    case ui::MotionEvent::Action::HOVER_ENTER:
+    case ui::MotionEvent::Action::HOVER_EXIT:
+    case ui::MotionEvent::Action::HOVER_MOVE:
+    case ui::MotionEvent::Action::BUTTON_PRESS:
+    case ui::MotionEvent::Action::BUTTON_RELEASE:
       NOTREACHED();
       return GestureEventDataPacket::INVALID;
   };
diff --git a/ui/events/gesture_detection/gesture_event_data_packet_unittest.cc b/ui/events/gesture_detection/gesture_event_data_packet_unittest.cc
index 1b7ec0e..ed46774 100644
--- a/ui/events/gesture_detection/gesture_event_data_packet_unittest.cc
+++ b/ui/events/gesture_detection/gesture_event_data_packet_unittest.cc
@@ -18,18 +18,11 @@
 const uint32_t uniqueTouchEventId = 1234U;
 
 GestureEventData CreateGesture(EventType type) {
-  return GestureEventData(GestureEventDetails(type),
-                          0,
-                          MotionEvent::TOOL_TYPE_FINGER,
-                          base::TimeTicks(),
-                          kTouchX,
-                          kTouchY,
-                          kTouchX + 5.f,
-                          kTouchY + 10.f,
-                          1,
+  return GestureEventData(GestureEventDetails(type), 0,
+                          MotionEvent::ToolType::FINGER, base::TimeTicks(),
+                          kTouchX, kTouchY, kTouchX + 5.f, kTouchY + 10.f, 1,
                           gfx::RectF(kTouchX - 1.f, kTouchY - 1.f, 2.f, 2.f),
-                          EF_NONE,
-                          uniqueTouchEventId);
+                          EF_NONE, uniqueTouchEventId);
 }
 
 }  // namespace
@@ -75,7 +68,7 @@
   EXPECT_EQ(GestureEventDataPacket::UNDEFINED, packet.gesture_source());
 
   packet = GestureEventDataPacket::FromTouch(
-      MockMotionEvent(MotionEvent::ACTION_DOWN, touch_time, kTouchX, kTouchY));
+      MockMotionEvent(MotionEvent::Action::DOWN, touch_time, kTouchX, kTouchY));
   EXPECT_TRUE(touch_time == packet.timestamp());
   EXPECT_EQ(0U, packet.gesture_count());
   EXPECT_EQ(gfx::PointF(kTouchX, kTouchY), packet.touch_location());
@@ -94,7 +87,7 @@
 
 TEST_F(GestureEventDataPacketTest, Copy) {
   GestureEventDataPacket packet0 = GestureEventDataPacket::FromTouch(
-      MockMotionEvent(MotionEvent::ACTION_UP));
+      MockMotionEvent(MotionEvent::Action::UP));
   packet0.Push(CreateGesture(ET_GESTURE_TAP_DOWN));
   packet0.Push(CreateGesture(ET_GESTURE_SCROLL_BEGIN));
 
@@ -107,30 +100,30 @@
 
 TEST_F(GestureEventDataPacketTest, GestureSource) {
   GestureEventDataPacket packet = GestureEventDataPacket::FromTouch(
-      MockMotionEvent(MotionEvent::ACTION_DOWN));
+      MockMotionEvent(MotionEvent::Action::DOWN));
   EXPECT_EQ(GestureEventDataPacket::TOUCH_SEQUENCE_START,
             packet.gesture_source());
 
   packet = GestureEventDataPacket::FromTouch(
-      MockMotionEvent(MotionEvent::ACTION_UP));
+      MockMotionEvent(MotionEvent::Action::UP));
   EXPECT_EQ(GestureEventDataPacket::TOUCH_SEQUENCE_END,
             packet.gesture_source());
 
   packet = GestureEventDataPacket::FromTouch(
-      MockMotionEvent(MotionEvent::ACTION_CANCEL));
+      MockMotionEvent(MotionEvent::Action::CANCEL));
   EXPECT_EQ(GestureEventDataPacket::TOUCH_SEQUENCE_CANCEL,
             packet.gesture_source());
 
   packet = GestureEventDataPacket::FromTouch(
-      MockMotionEvent(MotionEvent::ACTION_MOVE));
+      MockMotionEvent(MotionEvent::Action::MOVE));
   EXPECT_EQ(GestureEventDataPacket::TOUCH_MOVE, packet.gesture_source());
 
   packet = GestureEventDataPacket::FromTouch(
-      MockMotionEvent(MotionEvent::ACTION_POINTER_DOWN));
+      MockMotionEvent(MotionEvent::Action::POINTER_DOWN));
   EXPECT_EQ(GestureEventDataPacket::TOUCH_START, packet.gesture_source());
 
   packet = GestureEventDataPacket::FromTouch(
-      MockMotionEvent(MotionEvent::ACTION_POINTER_UP));
+      MockMotionEvent(MotionEvent::Action::POINTER_UP));
   EXPECT_EQ(GestureEventDataPacket::TOUCH_END, packet.gesture_source());
 
   GestureEventData gesture = CreateGesture(ET_GESTURE_TAP);
diff --git a/ui/events/gesture_detection/gesture_provider.cc b/ui/events/gesture_detection/gesture_provider.cc
index a83f385..d05af66 100644
--- a/ui/events/gesture_detection/gesture_provider.cc
+++ b/ui/events/gesture_detection/gesture_provider.cc
@@ -28,30 +28,30 @@
 
 const char* GetMotionEventActionName(MotionEvent::Action action) {
   switch (action) {
-    case MotionEvent::ACTION_NONE:
-      return "ACTION_NONE";
-    case MotionEvent::ACTION_POINTER_DOWN:
-      return "ACTION_POINTER_DOWN";
-    case MotionEvent::ACTION_POINTER_UP:
-      return "ACTION_POINTER_UP";
-    case MotionEvent::ACTION_DOWN:
-      return "ACTION_DOWN";
-    case MotionEvent::ACTION_UP:
-      return "ACTION_UP";
-    case MotionEvent::ACTION_CANCEL:
-      return "ACTION_CANCEL";
-    case MotionEvent::ACTION_MOVE:
-      return "ACTION_MOVE";
-    case MotionEvent::ACTION_HOVER_ENTER:
-      return "ACTION_HOVER_ENTER";
-    case MotionEvent::ACTION_HOVER_EXIT:
-      return "ACTION_HOVER_EXIT";
-    case MotionEvent::ACTION_HOVER_MOVE:
-      return "ACTION_HOVER_MOVE";
-    case MotionEvent::ACTION_BUTTON_PRESS:
-      return "ACTION_BUTTON_PRESS";
-    case MotionEvent::ACTION_BUTTON_RELEASE:
-      return "ACTION_BUTTON_RELEASE";
+    case MotionEvent::Action::NONE:
+      return "Action::NONE";
+    case MotionEvent::Action::POINTER_DOWN:
+      return "Action::POINTER_DOWN";
+    case MotionEvent::Action::POINTER_UP:
+      return "Action::POINTER_UP";
+    case MotionEvent::Action::DOWN:
+      return "Action::DOWN";
+    case MotionEvent::Action::UP:
+      return "Action::UP";
+    case MotionEvent::Action::CANCEL:
+      return "Action::CANCEL";
+    case MotionEvent::Action::MOVE:
+      return "Action::MOVE";
+    case MotionEvent::Action::HOVER_ENTER:
+      return "Action::HOVER_ENTER";
+    case MotionEvent::Action::HOVER_EXIT:
+      return "Action::HOVER_EXIT";
+    case MotionEvent::Action::HOVER_MOVE:
+      return "Action::HOVER_MOVE";
+    case MotionEvent::Action::BUTTON_PRESS:
+      return "Action::BUTTON_PRESS";
+    case MotionEvent::Action::BUTTON_RELEASE:
+      return "Action::BUTTON_RELEASE";
   }
   return "";
 }
@@ -118,7 +118,7 @@
       SetIgnoreSingleTap(true);
 
     const MotionEvent::Action action = event.GetAction();
-    if (action == MotionEvent::ACTION_DOWN) {
+    if (action == MotionEvent::Action::DOWN) {
       current_down_time_ = event.GetEventTime();
       current_longpress_time_ = base::TimeTicks();
       ignore_single_tap_ = false;
@@ -132,14 +132,14 @@
     gesture_detector_.OnTouchEvent(event);
     scale_gesture_detector_.OnTouchEvent(event);
 
-    if (action == MotionEvent::ACTION_UP ||
-        action == MotionEvent::ACTION_CANCEL) {
+    if (action == MotionEvent::Action::UP ||
+        action == MotionEvent::Action::CANCEL) {
       // Note: This call will have no effect if a fling was just generated, as
       // |Fling()| will have already signalled an end to touch-scrolling.
       if (scroll_event_sent_)
         Send(CreateGesture(ET_GESTURE_SCROLL_END, event));
       current_down_time_ = base::TimeTicks();
-    } else if (action == MotionEvent::ACTION_MOVE) {
+    } else if (action == MotionEvent::Action::MOVE) {
       if (!show_press_event_sent_ && !scroll_event_sent_) {
         max_diameter_before_show_press_ =
             std::max(max_diameter_before_show_press_, event.GetTouchMajor());
@@ -157,8 +157,8 @@
            gesture.type() == ET_GESTURE_BEGIN ||
            gesture.type() == ET_GESTURE_END);
 
-    if (gesture.primary_tool_type == MotionEvent::TOOL_TYPE_UNKNOWN ||
-        gesture.primary_tool_type == MotionEvent::TOOL_TYPE_FINGER) {
+    if (gesture.primary_tool_type == MotionEvent::ToolType::UNKNOWN ||
+        gesture.primary_tool_type == MotionEvent::ToolType::FINGER) {
       gesture.details.set_bounding_box(
           ClampBoundingBox(gesture.details.bounding_box_f(),
                            config_.min_gesture_bounds_length,
@@ -458,7 +458,7 @@
       }
     }
 
-    if (e.GetAction() == MotionEvent::ACTION_UP &&
+    if (e.GetAction() == MotionEvent::Action::UP &&
         !current_longpress_time_.is_null() &&
         !IsScaleGestureDetectionInProgress()) {
       GestureEventDetails long_tap_details(ET_GESTURE_LONG_TAP);
@@ -481,11 +481,11 @@
 
   bool OnDoubleTapEvent(const MotionEvent& e) override {
     switch (e.GetAction()) {
-      case MotionEvent::ACTION_DOWN:
+      case MotionEvent::Action::DOWN:
         gesture_detector_.set_longpress_enabled(false);
         break;
 
-      case MotionEvent::ACTION_UP:
+      case MotionEvent::Action::UP:
         if (!IsPinchInProgress() && !IsScrollInProgress()) {
           Send(CreateTapGesture(ET_GESTURE_DOUBLE_TAP, e, 1));
           return true;
@@ -804,9 +804,8 @@
 }
 
 void GestureProvider::ResetDetection() {
-  MotionEventGeneric generic_cancel_event(MotionEvent::ACTION_CANCEL,
-                                          base::TimeTicks::Now(),
-                                          PointerProperties());
+  MotionEventGeneric generic_cancel_event(
+      MotionEvent::Action::CANCEL, base::TimeTicks::Now(), PointerProperties());
   OnTouchEvent(generic_cancel_event);
 }
 
@@ -844,19 +843,20 @@
   // Aura requires one cancel event per touch point, whereas Android requires
   // one cancel event per touch sequence. Thus we need to allow extra cancel
   // events.
-  return current_down_event_ || event.GetAction() == MotionEvent::ACTION_DOWN ||
-         event.GetAction() == MotionEvent::ACTION_CANCEL;
+  return current_down_event_ ||
+         event.GetAction() == MotionEvent::Action::DOWN ||
+         event.GetAction() == MotionEvent::Action::CANCEL;
 }
 
 void GestureProvider::OnTouchEventHandlingBegin(const MotionEvent& event) {
   switch (event.GetAction()) {
-    case MotionEvent::ACTION_DOWN:
+    case MotionEvent::Action::DOWN:
       current_down_event_ = event.Clone();
       if (gesture_begin_end_types_enabled_)
         gesture_listener_->Send(
             gesture_listener_->CreateGesture(ET_GESTURE_BEGIN, event));
       break;
-    case MotionEvent::ACTION_POINTER_DOWN:
+    case MotionEvent::Action::POINTER_DOWN:
       if (gesture_begin_end_types_enabled_) {
         const int action_index = event.GetActionIndex();
         gesture_listener_->Send(gesture_listener_->CreateGesture(
@@ -873,17 +873,17 @@
             event.GetFlags()));
       }
       break;
-    case MotionEvent::ACTION_POINTER_UP:
-    case MotionEvent::ACTION_UP:
-    case MotionEvent::ACTION_CANCEL:
-    case MotionEvent::ACTION_MOVE:
+    case MotionEvent::Action::POINTER_UP:
+    case MotionEvent::Action::UP:
+    case MotionEvent::Action::CANCEL:
+    case MotionEvent::Action::MOVE:
       break;
-    case MotionEvent::ACTION_NONE:
-    case MotionEvent::ACTION_HOVER_ENTER:
-    case MotionEvent::ACTION_HOVER_EXIT:
-    case MotionEvent::ACTION_HOVER_MOVE:
-    case MotionEvent::ACTION_BUTTON_PRESS:
-    case MotionEvent::ACTION_BUTTON_RELEASE:
+    case MotionEvent::Action::NONE:
+    case MotionEvent::Action::HOVER_ENTER:
+    case MotionEvent::Action::HOVER_EXIT:
+    case MotionEvent::Action::HOVER_MOVE:
+    case MotionEvent::Action::BUTTON_PRESS:
+    case MotionEvent::Action::BUTTON_RELEASE:
       NOTREACHED();
       break;
   }
@@ -891,8 +891,8 @@
 
 void GestureProvider::OnTouchEventHandlingEnd(const MotionEvent& event) {
   switch (event.GetAction()) {
-    case MotionEvent::ACTION_UP:
-    case MotionEvent::ACTION_CANCEL: {
+    case MotionEvent::Action::UP:
+    case MotionEvent::Action::CANCEL: {
       if (gesture_begin_end_types_enabled_)
         gesture_listener_->Send(
             gesture_listener_->CreateGesture(ET_GESTURE_END, event));
@@ -902,21 +902,21 @@
       UpdateDoubleTapDetectionSupport();
       break;
     }
-    case MotionEvent::ACTION_POINTER_UP:
+    case MotionEvent::Action::POINTER_UP:
       if (gesture_begin_end_types_enabled_)
         gesture_listener_->Send(
             gesture_listener_->CreateGesture(ET_GESTURE_END, event));
       break;
-    case MotionEvent::ACTION_DOWN:
-    case MotionEvent::ACTION_POINTER_DOWN:
-    case MotionEvent::ACTION_MOVE:
+    case MotionEvent::Action::DOWN:
+    case MotionEvent::Action::POINTER_DOWN:
+    case MotionEvent::Action::MOVE:
       break;
-    case MotionEvent::ACTION_NONE:
-    case MotionEvent::ACTION_HOVER_ENTER:
-    case MotionEvent::ACTION_HOVER_EXIT:
-    case MotionEvent::ACTION_HOVER_MOVE:
-    case MotionEvent::ACTION_BUTTON_PRESS:
-    case MotionEvent::ACTION_BUTTON_RELEASE:
+    case MotionEvent::Action::NONE:
+    case MotionEvent::Action::HOVER_ENTER:
+    case MotionEvent::Action::HOVER_EXIT:
+    case MotionEvent::Action::HOVER_MOVE:
+    case MotionEvent::Action::BUTTON_PRESS:
+    case MotionEvent::Action::BUTTON_RELEASE:
       NOTREACHED();
       break;
   }
diff --git a/ui/events/gesture_detection/gesture_provider_unittest.cc b/ui/events/gesture_detection/gesture_provider_unittest.cc
index 39eae93..354c0ae 100644
--- a/ui/events/gesture_detection/gesture_provider_unittest.cc
+++ b/ui/events/gesture_detection/gesture_provider_unittest.cc
@@ -305,18 +305,17 @@
     int motion_event_flags = EF_SHIFT_DOWN | EF_CAPS_LOCK_ON;
 
     MockMotionEvent event =
-        ObtainMotionEvent(event_time, MotionEvent::ACTION_DOWN);
+        ObtainMotionEvent(event_time, MotionEvent::Action::DOWN);
     event.SetPrimaryPointerId(motion_event_id);
     event.set_flags(motion_event_flags);
 
     EXPECT_TRUE(gesture_provider_->OnTouchEvent(event));
     EXPECT_EQ(motion_event_flags, GetMostRecentGestureEvent().flags);
 
-    event = ObtainMotionEvent(event_time + kOneSecond,
-                              MotionEvent::ACTION_MOVE,
-                              scroll_to_x,
-                              scroll_to_y);
-    event.SetToolType(0, MotionEvent::TOOL_TYPE_FINGER);
+    event =
+        ObtainMotionEvent(event_time + kOneSecond, MotionEvent::Action::MOVE,
+                          scroll_to_x, scroll_to_y);
+    event.SetToolType(0, MotionEvent::ToolType::FINGER);
     event.SetPrimaryPointerId(motion_event_id);
     event.set_flags(motion_event_flags);
 
@@ -337,11 +336,11 @@
     EXPECT_EQ(ET_GESTURE_SCROLL_BEGIN, GetReceivedGesture(1).type());
     EXPECT_EQ(motion_event_id, GetReceivedGesture(1).motion_event_id);
     EXPECT_EQ(event_time + kOneSecond, GetReceivedGesture(1).time)
-        << "ScrollBegin should have the time of the ACTION_MOVE";
+        << "ScrollBegin should have the time of the Action::MOVE";
 
     event = ObtainMotionEvent(
         event_time + kOneSecond, end_action_type, scroll_to_x, scroll_to_y);
-    event.SetToolType(0, MotionEvent::TOOL_TYPE_FINGER);
+    event.SetToolType(0, MotionEvent::ToolType::FINGER);
     event.SetPrimaryPointerId(motion_event_id);
 
     gesture_provider_->OnTouchEvent(event);
@@ -399,11 +398,11 @@
       std::vector<gfx::PointF> event_positions(pointer_count);
       event_positions.assign(positions.begin(),
                              positions.begin() + pointer_count);
-      MockMotionEvent event =
-          ObtainMotionEvent(event_time,
-                            pointer_count > 1 ? MotionEvent::ACTION_POINTER_DOWN
-                                              : MotionEvent::ACTION_DOWN,
-                            event_positions);
+      MockMotionEvent event = ObtainMotionEvent(
+          event_time,
+          pointer_count > 1 ? MotionEvent::Action::POINTER_DOWN
+                            : MotionEvent::Action::DOWN,
+          event_positions);
       EXPECT_TRUE(gesture_provider_->OnTouchEvent(event));
     }
 
@@ -411,20 +410,17 @@
       positions[i] += gfx::ScaleVector2d(velocities[i], dt);
     MockMotionEvent event =
         ObtainMotionEvent(event_time + kDeltaTimeForFlingSequences,
-                          MotionEvent::ACTION_MOVE,
-                          positions);
+                          MotionEvent::Action::MOVE, positions);
     EXPECT_TRUE(gesture_provider_->OnTouchEvent(event));
 
     for (size_t i = 0; i < positions.size(); ++i)
       positions[i] += gfx::ScaleVector2d(velocities[i], dt);
     event = ObtainMotionEvent(event_time + 2 * kDeltaTimeForFlingSequences,
-                              MotionEvent::ACTION_MOVE,
-                              positions);
+                              MotionEvent::Action::MOVE, positions);
     EXPECT_TRUE(gesture_provider_->OnTouchEvent(event));
 
     event = ObtainMotionEvent(event_time + 2 * kDeltaTimeForFlingSequences,
-                              MotionEvent::ACTION_POINTER_UP,
-                              positions);
+                              MotionEvent::Action::POINTER_UP, positions);
     EXPECT_TRUE(gesture_provider_->OnTouchEvent(event));
   }
 
@@ -449,8 +445,8 @@
   gesture_provider_->SetDoubleTapSupportForPlatformEnabled(false);
 
   MockMotionEvent event =
-      ObtainMotionEvent(event_time, MotionEvent::ACTION_DOWN);
-  event.SetToolType(0, MotionEvent::TOOL_TYPE_FINGER);
+      ObtainMotionEvent(event_time, MotionEvent::Action::DOWN);
+  event.SetToolType(0, MotionEvent::ToolType::FINGER);
   event.SetPrimaryPointerId(motion_event_id);
   event.set_flags(motion_event_flags);
 
@@ -463,9 +459,9 @@
   EXPECT_EQ(BoundsForSingleMockTouchAtLocation(kFakeCoordX, kFakeCoordY),
             GetMostRecentGestureEvent().details.bounding_box_f());
 
-  event = ObtainMotionEvent(event_time + kOneMicrosecond,
-                            MotionEvent::ACTION_UP);
-  event.SetToolType(0, MotionEvent::TOOL_TYPE_FINGER);
+  event =
+      ObtainMotionEvent(event_time + kOneMicrosecond, MotionEvent::Action::UP);
+  event.SetToolType(0, MotionEvent::ToolType::FINGER);
   event.SetPrimaryPointerId(motion_event_id);
   event.set_flags(motion_event_flags);
 
@@ -492,7 +488,7 @@
   gesture_provider_->SetDoubleTapSupportForPlatformEnabled(true);
 
   MockMotionEvent event =
-      ObtainMotionEvent(event_time, MotionEvent::ACTION_DOWN);
+      ObtainMotionEvent(event_time, MotionEvent::Action::DOWN);
   event.SetPrimaryPointerId(motion_event_id);
   event.set_flags(motion_event_flags);
 
@@ -504,8 +500,8 @@
   EXPECT_EQ(BoundsForSingleMockTouchAtLocation(kFakeCoordX, kFakeCoordY),
             GetMostRecentGestureEvent().details.bounding_box_f());
 
-  event = ObtainMotionEvent(event_time + kOneMicrosecond,
-                            MotionEvent::ACTION_UP);
+  event =
+      ObtainMotionEvent(event_time + kOneMicrosecond, MotionEvent::Action::UP);
   event.SetPrimaryPointerId(motion_event_id);
   event.set_flags(motion_event_flags);
 
@@ -535,7 +531,7 @@
   int motion_event_flags = EF_ALT_DOWN;
 
   MockMotionEvent event =
-      ObtainMotionEvent(event_time, MotionEvent::ACTION_DOWN);
+      ObtainMotionEvent(event_time, MotionEvent::Action::DOWN);
   event.SetPrimaryPointerId(motion_event_id);
   event.set_flags(motion_event_flags);
 
@@ -545,18 +541,15 @@
   EXPECT_EQ(motion_event_flags, GetMostRecentGestureEvent().flags);
   EXPECT_EQ(1, GetMostRecentGestureEvent().details.touch_points());
 
-  event = ObtainMotionEvent(event_time + delta_time,
-                            MotionEvent::ACTION_MOVE,
-                            kFakeCoordX * 10,
-                            kFakeCoordY * 10);
+  event = ObtainMotionEvent(event_time + delta_time, MotionEvent::Action::MOVE,
+                            kFakeCoordX * 10, kFakeCoordY * 10);
   event.SetPrimaryPointerId(motion_event_id);
   event.set_flags(motion_event_flags);
   EXPECT_TRUE(gesture_provider_->OnTouchEvent(event));
 
-  event = ObtainMotionEvent(event_time + delta_time * 2,
-                            MotionEvent::ACTION_UP,
-                            kFakeCoordX * 10,
-                            kFakeCoordY * 10);
+  event =
+      ObtainMotionEvent(event_time + delta_time * 2, MotionEvent::Action::UP,
+                        kFakeCoordX * 10, kFakeCoordY * 10);
   event.SetPrimaryPointerId(motion_event_id);
   event.set_flags(motion_event_flags);
 
@@ -576,7 +569,7 @@
 // - ET_GESTURE_SCROLL_UPDATE
 // - ET_GESTURE_SCROLL_END
 TEST_F(GestureProviderTest, ScrollEventActionUpSequence) {
-  CheckScrollEventSequenceForEndActionType(MotionEvent::ACTION_UP);
+  CheckScrollEventSequenceForEndActionType(MotionEvent::Action::UP);
 }
 
 // Verify that for a cancelled scroll the following events are sent:
@@ -584,7 +577,7 @@
 // - ET_GESTURE_SCROLL_UPDATE
 // - ET_GESTURE_SCROLL_END
 TEST_F(GestureProviderTest, ScrollEventActionCancelSequence) {
-  CheckScrollEventSequenceForEndActionType(MotionEvent::ACTION_CANCEL);
+  CheckScrollEventSequenceForEndActionType(MotionEvent::Action::CANCEL);
 }
 
 // Verify that for a normal fling (fling after scroll) the following events are
@@ -597,15 +590,13 @@
   int motion_event_id = 6;
 
   MockMotionEvent event =
-      ObtainMotionEvent(event_time, MotionEvent::ACTION_DOWN);
+      ObtainMotionEvent(event_time, MotionEvent::Action::DOWN);
   event.SetPrimaryPointerId(motion_event_id);
 
   EXPECT_TRUE(gesture_provider_->OnTouchEvent(event));
 
-  event = ObtainMotionEvent(event_time + delta_time,
-                            MotionEvent::ACTION_MOVE,
-                            kFakeCoordX * 5,
-                            kFakeCoordY * 5);
+  event = ObtainMotionEvent(event_time + delta_time, MotionEvent::Action::MOVE,
+                            kFakeCoordX * 5, kFakeCoordY * 5);
   event.SetPrimaryPointerId(motion_event_id);
 
   EXPECT_TRUE(gesture_provider_->OnTouchEvent(event));
@@ -624,10 +615,9 @@
   EXPECT_TRUE(hint_x > 0 && hint_y > 0 && hint_x > hint_y)
       << "ScrollBegin hint should be in positive X axis";
 
-  event = ObtainMotionEvent(event_time + delta_time * 2,
-                            MotionEvent::ACTION_UP,
-                            kFakeCoordX * 10,
-                            kFakeCoordY * 10);
+  event =
+      ObtainMotionEvent(event_time + delta_time * 2, MotionEvent::Action::UP,
+                        kFakeCoordX * 10, kFakeCoordY * 10);
   event.SetPrimaryPointerId(motion_event_id);
 
   EXPECT_TRUE(gesture_provider_->OnTouchEvent(event));
@@ -637,14 +627,14 @@
   EXPECT_EQ(1, GetMostRecentGestureEvent().details.touch_points());
   EXPECT_FALSE(HasReceivedGesture(ET_GESTURE_SCROLL_END));
   EXPECT_EQ(event_time + delta_time * 2, GetMostRecentGestureEvent().time)
-      << "FlingStart should have the time of the ACTION_UP";
+      << "FlingStart should have the time of the Action::UP";
 }
 
 TEST_F(GestureProviderTest, GestureCancelledOnCancelEvent) {
   const base::TimeTicks event_time = TimeTicks::Now();
 
   MockMotionEvent event =
-      ObtainMotionEvent(event_time, MotionEvent::ACTION_DOWN);
+      ObtainMotionEvent(event_time, MotionEvent::Action::DOWN);
   EXPECT_TRUE(gesture_provider_->OnTouchEvent(event));
   EXPECT_EQ(ET_GESTURE_TAP_DOWN, GetMostRecentGestureEventType());
 
@@ -658,9 +648,9 @@
   EXPECT_TRUE(CancelActiveTouchSequence());
   EXPECT_FALSE(HasDownEvent());
 
-  // A final ACTION_UP should have no effect.
+  // A final Action::UP should have no effect.
   event = ObtainMotionEvent(event_time + kOneMicrosecond * 2,
-                            MotionEvent::ACTION_UP);
+                            MotionEvent::Action::UP);
   EXPECT_FALSE(gesture_provider_->OnTouchEvent(event));
 }
 
@@ -668,7 +658,7 @@
   const base::TimeTicks event_time = TimeTicks::Now();
 
   MockMotionEvent event =
-      ObtainMotionEvent(event_time, MotionEvent::ACTION_DOWN);
+      ObtainMotionEvent(event_time, MotionEvent::Action::DOWN);
   EXPECT_TRUE(gesture_provider_->OnTouchEvent(event));
   EXPECT_EQ(ET_GESTURE_TAP_DOWN, GetMostRecentGestureEventType());
 
@@ -680,9 +670,9 @@
   ResetGestureDetection();
   EXPECT_FALSE(HasDownEvent());
 
-  // A final ACTION_UP should have no effect.
+  // A final Action::UP should have no effect.
   event = ObtainMotionEvent(event_time + kOneMicrosecond * 2,
-                            MotionEvent::ACTION_UP);
+                            MotionEvent::Action::UP);
   EXPECT_FALSE(gesture_provider_->OnTouchEvent(event));
 }
 
@@ -690,23 +680,20 @@
   base::TimeTicks event_time = base::TimeTicks::Now();
 
   MockMotionEvent event =
-      ObtainMotionEvent(event_time, MotionEvent::ACTION_DOWN);
+      ObtainMotionEvent(event_time, MotionEvent::Action::DOWN);
 
   EXPECT_TRUE(gesture_provider_->OnTouchEvent(event));
 
   EXPECT_EQ(ET_GESTURE_TAP_DOWN, GetMostRecentGestureEventType());
   EXPECT_EQ(1, GetMostRecentGestureEvent().details.touch_points());
-  event = ObtainMotionEvent(event_time + kOneMicrosecond,
-                            MotionEvent::ACTION_MOVE,
-                            kFakeCoordX + 50,
-                            kFakeCoordY + 50);
+  event =
+      ObtainMotionEvent(event_time + kOneMicrosecond, MotionEvent::Action::MOVE,
+                        kFakeCoordX + 50, kFakeCoordY + 50);
   EXPECT_TRUE(gesture_provider_->OnTouchEvent(event));
   EXPECT_EQ(ET_GESTURE_SCROLL_UPDATE, GetMostRecentGestureEventType());
 
-  event = ObtainMotionEvent(event_time + kOneSecond,
-                            MotionEvent::ACTION_UP,
-                            kFakeCoordX + 50,
-                            kFakeCoordY + 50);
+  event = ObtainMotionEvent(event_time + kOneSecond, MotionEvent::Action::UP,
+                            kFakeCoordX + 50, kFakeCoordY + 50);
   EXPECT_TRUE(gesture_provider_->OnTouchEvent(event));
   EXPECT_EQ(ET_GESTURE_SCROLL_END, GetMostRecentGestureEventType());
   EXPECT_FALSE(HasReceivedGesture(ET_GESTURE_LONG_TAP));
@@ -716,24 +703,20 @@
   base::TimeTicks event_time = base::TimeTicks::Now();
 
   MockMotionEvent event =
-      ObtainMotionEvent(event_time, MotionEvent::ACTION_DOWN);
+      ObtainMotionEvent(event_time, MotionEvent::Action::DOWN);
   EXPECT_TRUE(gesture_provider_->OnTouchEvent(event));
 
   EXPECT_EQ(ET_GESTURE_TAP_DOWN, GetMostRecentGestureEventType());
   EXPECT_EQ(1, GetMostRecentGestureEvent().details.touch_points());
 
   event = ObtainMotionEvent(event_time + kOneMicrosecond,
-                            MotionEvent::ACTION_UP,
-                            kFakeCoordX,
-                            kFakeCoordY);
+                            MotionEvent::Action::UP, kFakeCoordX, kFakeCoordY);
   gesture_provider_->OnTouchEvent(event);
   EXPECT_EQ(ET_GESTURE_TAP_UNCONFIRMED, GetMostRecentGestureEventType());
   EXPECT_EQ(1, GetMostRecentGestureEvent().details.touch_points());
 
   event_time += GetValidDoubleTapDelay();
-  event = ObtainMotionEvent(event_time,
-                            MotionEvent::ACTION_DOWN,
-                            kFakeCoordX,
+  event = ObtainMotionEvent(event_time, MotionEvent::Action::DOWN, kFakeCoordX,
                             kFakeCoordY);
   EXPECT_TRUE(gesture_provider_->OnTouchEvent(event));
   EXPECT_EQ(ET_GESTURE_TAP_DOWN, GetMostRecentGestureEventType());
@@ -741,18 +724,16 @@
 
   // Moving a very small amount of distance should not trigger the double tap
   // drag zoom mode.
-  event = ObtainMotionEvent(event_time + kOneMicrosecond,
-                            MotionEvent::ACTION_MOVE,
-                            kFakeCoordX,
-                            kFakeCoordY + 1);
+  event =
+      ObtainMotionEvent(event_time + kOneMicrosecond, MotionEvent::Action::MOVE,
+                        kFakeCoordX, kFakeCoordY + 1);
   EXPECT_TRUE(gesture_provider_->OnTouchEvent(event));
   EXPECT_EQ(ET_GESTURE_TAP_DOWN, GetMostRecentGestureEventType());
   EXPECT_EQ(1, GetMostRecentGestureEvent().details.touch_points());
 
-  event = ObtainMotionEvent(event_time + kOneMicrosecond * 2,
-                            MotionEvent::ACTION_UP,
-                            kFakeCoordX,
-                            kFakeCoordY + 1);
+  event =
+      ObtainMotionEvent(event_time + kOneMicrosecond * 2,
+                        MotionEvent::Action::UP, kFakeCoordX, kFakeCoordY + 1);
   EXPECT_TRUE(gesture_provider_->OnTouchEvent(event));
 
   const GestureEventData& double_tap = GetMostRecentGestureEvent();
@@ -768,26 +749,23 @@
   const base::TimeTicks down_time_2 = down_time_1 + GetValidDoubleTapDelay();
 
   MockMotionEvent event =
-      ObtainMotionEvent(down_time_1, MotionEvent::ACTION_DOWN);
+      ObtainMotionEvent(down_time_1, MotionEvent::Action::DOWN);
   EXPECT_TRUE(gesture_provider_->OnTouchEvent(event));
 
   event = ObtainMotionEvent(down_time_1 + kOneMicrosecond,
-                            MotionEvent::ACTION_UP,
-                            kFakeCoordX,
-                            kFakeCoordY);
+                            MotionEvent::Action::UP, kFakeCoordX, kFakeCoordY);
   gesture_provider_->OnTouchEvent(event);
   EXPECT_EQ(ET_GESTURE_TAP_UNCONFIRMED, GetMostRecentGestureEventType());
   EXPECT_EQ(1, GetMostRecentGestureEvent().details.touch_points());
 
-  event = ObtainMotionEvent(
-      down_time_2, MotionEvent::ACTION_DOWN, kFakeCoordX, kFakeCoordY);
+  event = ObtainMotionEvent(down_time_2, MotionEvent::Action::DOWN, kFakeCoordX,
+                            kFakeCoordY);
   EXPECT_TRUE(gesture_provider_->OnTouchEvent(event));
   EXPECT_EQ(ET_GESTURE_TAP_DOWN, GetMostRecentGestureEventType());
   EXPECT_EQ(1, GetMostRecentGestureEvent().details.touch_points());
 
   event = ObtainMotionEvent(down_time_2 + kOneMicrosecond,
-                            MotionEvent::ACTION_MOVE,
-                            kFakeCoordX,
+                            MotionEvent::Action::MOVE, kFakeCoordX,
                             kFakeCoordY + 100);
   EXPECT_TRUE(gesture_provider_->OnTouchEvent(event));
   EXPECT_TRUE(HasReceivedGesture(ET_GESTURE_SCROLL_BEGIN));
@@ -798,8 +776,7 @@
             GetMostRecentGestureEvent().details.bounding_box_f());
 
   event = ObtainMotionEvent(down_time_2 + kOneMicrosecond * 2,
-                            MotionEvent::ACTION_MOVE,
-                            kFakeCoordX,
+                            MotionEvent::Action::MOVE, kFakeCoordX,
                             kFakeCoordY + 200);
   EXPECT_TRUE(gesture_provider_->OnTouchEvent(event));
   ASSERT_EQ(ET_GESTURE_PINCH_UPDATE, GetMostRecentGestureEventType());
@@ -808,8 +785,7 @@
             GetMostRecentGestureEvent().details.bounding_box_f());
 
   event = ObtainMotionEvent(down_time_2 + kOneMicrosecond * 3,
-                            MotionEvent::ACTION_MOVE,
-                            kFakeCoordX,
+                            MotionEvent::Action::MOVE, kFakeCoordX,
                             kFakeCoordY + 100);
   EXPECT_TRUE(gesture_provider_->OnTouchEvent(event));
   ASSERT_EQ(ET_GESTURE_PINCH_UPDATE, GetMostRecentGestureEventType());
@@ -818,8 +794,7 @@
             GetMostRecentGestureEvent().details.bounding_box_f());
 
   event = ObtainMotionEvent(down_time_2 + kOneMicrosecond * 4,
-                            MotionEvent::ACTION_UP,
-                            kFakeCoordX,
+                            MotionEvent::Action::UP, kFakeCoordX,
                             kFakeCoordY - 200);
   EXPECT_TRUE(gesture_provider_->OnTouchEvent(event));
   EXPECT_TRUE(HasReceivedGesture(ET_GESTURE_PINCH_END));
@@ -839,20 +814,18 @@
   const base::TimeTicks event_time = TimeTicks::Now();
 
   MockMotionEvent event =
-      ObtainMotionEvent(event_time, MotionEvent::ACTION_DOWN);
+      ObtainMotionEvent(event_time, MotionEvent::Action::DOWN);
   EXPECT_TRUE(gesture_provider_->OnTouchEvent(event));
 
   // Move twice so that we get two ET_GESTURE_SCROLL_UPDATE events and can
   // compare the relative and absolute coordinates.
-  event = ObtainMotionEvent(event_time + kOneMicrosecond,
-                            MotionEvent::ACTION_MOVE,
-                            kFakeCoordX - delta_x / 2,
-                            kFakeCoordY - delta_y / 2);
+  event =
+      ObtainMotionEvent(event_time + kOneMicrosecond, MotionEvent::Action::MOVE,
+                        kFakeCoordX - delta_x / 2, kFakeCoordY - delta_y / 2);
   EXPECT_TRUE(gesture_provider_->OnTouchEvent(event));
 
   event = ObtainMotionEvent(event_time + kOneMicrosecond * 2,
-                            MotionEvent::ACTION_MOVE,
-                            kFakeCoordX - delta_x,
+                            MotionEvent::Action::MOVE, kFakeCoordX - delta_x,
                             kFakeCoordY - delta_y);
   event.SetRawOffset(raw_offset_x, raw_offset_y);
   EXPECT_TRUE(gesture_provider_->OnTouchEvent(event));
@@ -882,26 +855,22 @@
   const base::TimeTicks event_time = TimeTicks::Now();
 
   MockMotionEvent event =
-      ObtainMotionEvent(event_time, MotionEvent::ACTION_DOWN);
+      ObtainMotionEvent(event_time, MotionEvent::Action::DOWN);
   EXPECT_TRUE(gesture_provider_->OnTouchEvent(event));
 
   // Skip past the touch slop and move back.
-  event = ObtainMotionEvent(event_time,
-                            MotionEvent::ACTION_MOVE,
-                            kFakeCoordX,
+  event = ObtainMotionEvent(event_time, MotionEvent::Action::MOVE, kFakeCoordX,
                             kFakeCoordY + 100);
   EXPECT_TRUE(gesture_provider_->OnTouchEvent(event));
-  event = ObtainMotionEvent(event_time,
-                            MotionEvent::ACTION_MOVE);
+  event = ObtainMotionEvent(event_time, MotionEvent::Action::MOVE);
   EXPECT_TRUE(gesture_provider_->OnTouchEvent(event));
 
   // Now move up slowly, mostly vertically but with a (fractional) bit of
   // horizontal motion.
   for (int i = 1; i <= 10; i++) {
-    event = ObtainMotionEvent(event_time + kOneMicrosecond * i,
-                              MotionEvent::ACTION_MOVE,
-                              kFakeCoordX + delta_x * i,
-                              kFakeCoordY + delta_y * i);
+    event = ObtainMotionEvent(
+        event_time + kOneMicrosecond * i, MotionEvent::Action::MOVE,
+        kFakeCoordX + delta_x * i, kFakeCoordY + delta_y * i);
     EXPECT_TRUE(gesture_provider_->OnTouchEvent(event));
 
     ASSERT_LT(0U, GetReceivedGestureCount());
@@ -937,21 +906,19 @@
   const base::TimeTicks event_time = TimeTicks::Now();
 
   MockMotionEvent event =
-      ObtainMotionEvent(event_time, MotionEvent::ACTION_DOWN);
+      ObtainMotionEvent(event_time, MotionEvent::Action::DOWN);
   EXPECT_TRUE(gesture_provider_->OnTouchEvent(event));
 
   // Move twice such that the first event isn't sufficient to start
   // scrolling on it's own.
-  event = ObtainMotionEvent(event_time + kOneMicrosecond,
-                            MotionEvent::ACTION_MOVE,
-                            kFakeCoordX + 2,
-                            kFakeCoordY + 1);
+  event =
+      ObtainMotionEvent(event_time + kOneMicrosecond, MotionEvent::Action::MOVE,
+                        kFakeCoordX + 2, kFakeCoordY + 1);
   EXPECT_TRUE(gesture_provider_->OnTouchEvent(event));
   EXPECT_FALSE(gesture_provider_->IsScrollInProgress());
 
   event = ObtainMotionEvent(event_time + kOneMicrosecond * 2,
-                            MotionEvent::ACTION_MOVE,
-                            kFakeCoordX + delta_x,
+                            MotionEvent::Action::MOVE, kFakeCoordX + delta_x,
                             kFakeCoordY + delta_y);
   EXPECT_TRUE(gesture_provider_->OnTouchEvent(event));
   EXPECT_TRUE(gesture_provider_->IsScrollInProgress());
@@ -973,20 +940,20 @@
   base::TimeTicks event_time = base::TimeTicks::Now();
 
   MockMotionEvent event =
-      ObtainMotionEvent(event_time, MotionEvent::ACTION_DOWN, 0, 0);
+      ObtainMotionEvent(event_time, MotionEvent::Action::DOWN, 0, 0);
   EXPECT_TRUE(gesture_provider_->OnTouchEvent(event));
 
   // Move within slop region.
-  event = ObtainMotionEvent(event_time, MotionEvent::ACTION_MOVE, 0,
+  event = ObtainMotionEvent(event_time, MotionEvent::Action::MOVE, 0,
                             scaled_touch_slop / 2);
   EXPECT_TRUE(gesture_provider_->OnTouchEvent(event));
 
   // Exceed slop region.
-  event = ObtainMotionEvent(event_time, MotionEvent::ACTION_MOVE, 0,
+  event = ObtainMotionEvent(event_time, MotionEvent::Action::MOVE, 0,
                             2 * scaled_touch_slop);
   EXPECT_TRUE(gesture_provider_->OnTouchEvent(event));
 
-  event = ObtainMotionEvent(event_time, MotionEvent::ACTION_UP, 0,
+  event = ObtainMotionEvent(event_time, MotionEvent::Action::UP, 0,
                             2 * scaled_touch_slop);
   EXPECT_TRUE(gesture_provider_->OnTouchEvent(event));
 
@@ -1004,41 +971,41 @@
   base::TimeTicks event_time = base::TimeTicks::Now();
 
   MockMotionEvent event =
-      ObtainMotionEvent(event_time, MotionEvent::ACTION_DOWN, 0, 0);
+      ObtainMotionEvent(event_time, MotionEvent::Action::DOWN, 0, 0);
   EXPECT_TRUE(gesture_provider_->OnTouchEvent(event));
 
-  event = ObtainMotionEvent(event_time, MotionEvent::ACTION_POINTER_DOWN, 0, 0,
+  event = ObtainMotionEvent(event_time, MotionEvent::Action::POINTER_DOWN, 0, 0,
                             kMaxTwoFingerTapSeparation / 2, 0);
   EXPECT_TRUE(gesture_provider_->OnTouchEvent(event));
 
   // Move within slop region: two-finger tap happens.
-  event = ObtainMotionEvent(event_time, MotionEvent::ACTION_MOVE, 0,
+  event = ObtainMotionEvent(event_time, MotionEvent::Action::MOVE, 0,
                             scaled_touch_slop / 2,
                             kMaxTwoFingerTapSeparation / 2, 0);
   EXPECT_TRUE(gesture_provider_->OnTouchEvent(event));
 
-  event = ObtainMotionEvent(event_time, MotionEvent::ACTION_POINTER_UP, 0,
+  event = ObtainMotionEvent(event_time, MotionEvent::Action::POINTER_UP, 0,
                             scaled_touch_slop / 2,
                             kMaxTwoFingerTapSeparation / 2, 0);
   EXPECT_TRUE(gesture_provider_->OnTouchEvent(event));
 
   // Exceed slop region: scroll.
-  event = ObtainMotionEvent(event_time, MotionEvent::ACTION_POINTER_DOWN, 0,
+  event = ObtainMotionEvent(event_time, MotionEvent::Action::POINTER_DOWN, 0,
                             scaled_touch_slop / 2,
                             kMaxTwoFingerTapSeparation / 2, 0);
   EXPECT_TRUE(gesture_provider_->OnTouchEvent(event));
 
   event = ObtainMotionEvent(
-      event_time, MotionEvent::ACTION_MOVE, 0, scaled_touch_slop / 2,
+      event_time, MotionEvent::Action::MOVE, 0, scaled_touch_slop / 2,
       kMaxTwoFingerTapSeparation / 2, 2 * scaled_touch_slop);
   EXPECT_TRUE(gesture_provider_->OnTouchEvent(event));
 
   event = ObtainMotionEvent(
-      event_time, MotionEvent::ACTION_POINTER_UP, 0, scaled_touch_slop / 2,
+      event_time, MotionEvent::Action::POINTER_UP, 0, scaled_touch_slop / 2,
       kMaxTwoFingerTapSeparation / 2, 2 * scaled_touch_slop);
   EXPECT_TRUE(gesture_provider_->OnTouchEvent(event));
 
-  event = ObtainMotionEvent(event_time, MotionEvent::ACTION_UP, 0,
+  event = ObtainMotionEvent(event_time, MotionEvent::Action::UP, 0,
                             scaled_touch_slop / 2);
   EXPECT_TRUE(gesture_provider_->OnTouchEvent(event));
 
@@ -1059,13 +1026,13 @@
   base::TimeTicks event_time = base::TimeTicks::Now();
 
   MockMotionEvent event =
-      ObtainMotionEvent(event_time, MotionEvent::ACTION_DOWN, 0, 0);
+      ObtainMotionEvent(event_time, MotionEvent::Action::DOWN, 0, 0);
   EXPECT_TRUE(gesture_provider_->OnTouchEvent(event));
 
-  // Don't send ACTION_POINTER_DOWN event to the gesture_provider.
-  // This is for simulating the cases that the ACTION_POINTER_DOWN event is
+  // Don't send Action::POINTER_DOWN event to the gesture_provider.
+  // This is for simulating the cases that the Action::POINTER_DOWN event is
   // missing from the event sequence.
-  event = ObtainMotionEvent(event_time, MotionEvent::ACTION_POINTER_DOWN, 0, 0,
+  event = ObtainMotionEvent(event_time, MotionEvent::Action::POINTER_DOWN, 0, 0,
                             kMaxTwoFingerTapSeparation / 2, 0);
 
   event.MovePoint(1, 0, 3 * scaled_touch_slop);
@@ -1091,21 +1058,21 @@
   base::TimeTicks event_time = base::TimeTicks::Now();
 
   MockMotionEvent event =
-      ObtainMotionEvent(event_time, MotionEvent::ACTION_DOWN, 0, 0);
+      ObtainMotionEvent(event_time, MotionEvent::Action::DOWN, 0, 0);
   EXPECT_TRUE(gesture_provider_->OnTouchEvent(event));
 
-  event = ObtainMotionEvent(event_time, MotionEvent::ACTION_POINTER_DOWN, 0, 0,
+  event = ObtainMotionEvent(event_time, MotionEvent::Action::POINTER_DOWN, 0, 0,
                             kMaxTwoFingerTapSeparation / 2, 0);
   EXPECT_TRUE(gesture_provider_->OnTouchEvent(event));
 
-  event = ObtainMotionEvent(event_time, MotionEvent::ACTION_POINTER_DOWN, 0, 0,
+  event = ObtainMotionEvent(event_time, MotionEvent::Action::POINTER_DOWN, 0, 0,
                             kMaxTwoFingerTapSeparation / 2, 0,
                             2 * kMaxTwoFingerTapSeparation, 0);
   EXPECT_TRUE(gesture_provider_->OnTouchEvent(event));
 
   // Move within slop region, three-finger scroll always happens.
   event = ObtainMotionEvent(
-      event_time, MotionEvent::ACTION_MOVE, 0, scaled_touch_slop / 2,
+      event_time, MotionEvent::Action::MOVE, 0, scaled_touch_slop / 2,
       kMaxTwoFingerTapSeparation / 2, 0, 2 * kMaxTwoFingerTapSeparation, 0);
   EXPECT_TRUE(gesture_provider_->OnTouchEvent(event));
 
@@ -1122,10 +1089,10 @@
   base::TimeTicks event_time = base::TimeTicks::Now();
 
   MockMotionEvent event =
-      ObtainMotionEvent(event_time, MotionEvent::ACTION_DOWN, 0, 0);
+      ObtainMotionEvent(event_time, MotionEvent::Action::DOWN, 0, 0);
   EXPECT_TRUE(gesture_provider_->OnTouchEvent(event));
 
-  event = ObtainMotionEvent(event_time, MotionEvent::ACTION_POINTER_DOWN, 0, 0,
+  event = ObtainMotionEvent(event_time, MotionEvent::Action::POINTER_DOWN, 0, 0,
                             kMaxTwoFingerTapSeparation / 2, 0);
   EXPECT_TRUE(gesture_provider_->OnTouchEvent(event));
 
@@ -1152,21 +1119,21 @@
   base::TimeTicks event_time = base::TimeTicks::Now();
   base::TimeDelta delta_time = kDeltaTimeForFlingSequences;
   MockMotionEvent event = ObtainMotionEvent(event_time + delta_time,
-                                            MotionEvent::ACTION_DOWN, 0, 0);
+                                            MotionEvent::Action::DOWN, 0, 0);
   EXPECT_TRUE(gesture_provider_->OnTouchEvent(event));
   event = ObtainMotionEvent(event_time + 2 * delta_time,
-                            MotionEvent::ACTION_POINTER_DOWN, 0, 0, 10, 0);
+                            MotionEvent::Action::POINTER_DOWN, 0, 0, 10, 0);
   EXPECT_TRUE(gesture_provider_->OnTouchEvent(event));
 
   // Fast pointer movements within touch slop region.
   event = ObtainMotionEvent(event_time + 3 * delta_time,
-                            MotionEvent::ACTION_MOVE, 1, 0, 11, 0);
+                            MotionEvent::Action::MOVE, 1, 0, 11, 0);
   EXPECT_TRUE(gesture_provider_->OnTouchEvent(event));
   event = ObtainMotionEvent(event_time + 4 * delta_time,
-                            MotionEvent::ACTION_MOVE, 2, 0, 12, 0);
+                            MotionEvent::Action::MOVE, 2, 0, 12, 0);
   EXPECT_TRUE(gesture_provider_->OnTouchEvent(event));
   event = ObtainMotionEvent(event_time + 5 * delta_time,
-                            MotionEvent::ACTION_MOVE, 3, 0, 13, 0);
+                            MotionEvent::Action::MOVE, 3, 0, 13, 0);
   EXPECT_TRUE(gesture_provider_->OnTouchEvent(event));
 
   event.ReleasePointAtIndex(0);
@@ -1184,16 +1151,14 @@
   base::TimeTicks event_time = base::TimeTicks::Now();
 
   MockMotionEvent event =
-      ObtainMotionEvent(event_time, MotionEvent::ACTION_DOWN);
+      ObtainMotionEvent(event_time, MotionEvent::Action::DOWN);
   EXPECT_TRUE(gesture_provider_->OnTouchEvent(event));
-  event = ObtainMotionEvent(event_time + kOneMicrosecond,
-                            MotionEvent::ACTION_MOVE,
-                            kFakeCoordX * 5,
-                            kFakeCoordY * 5);
+  event =
+      ObtainMotionEvent(event_time + kOneMicrosecond, MotionEvent::Action::MOVE,
+                        kFakeCoordX * 5, kFakeCoordY * 5);
   EXPECT_TRUE(gesture_provider_->OnTouchEvent(event));
   event = ObtainMotionEvent(event_time + kOneMicrosecond * 2,
-                            MotionEvent::ACTION_MOVE,
-                            kFakeCoordX * 10,
+                            MotionEvent::Action::MOVE, kFakeCoordX * 10,
                             kFakeCoordY * 10);
   EXPECT_TRUE(gesture_provider_->OnTouchEvent(event));
 
@@ -1211,7 +1176,7 @@
   base::TimeTicks event_time = base::TimeTicks::Now();
 
   MockMotionEvent event =
-      ObtainMotionEvent(event_time, MotionEvent::ACTION_DOWN);
+      ObtainMotionEvent(event_time, MotionEvent::Action::DOWN);
   EXPECT_TRUE(gesture_provider_->OnTouchEvent(event));
 
   const base::TimeDelta long_press_timeout =
@@ -1223,7 +1188,7 @@
   EXPECT_EQ(BoundsForSingleMockTouchAtLocation(kFakeCoordX, kFakeCoordY),
             GetMostRecentGestureEvent().details.bounding_box_f());
 
-  event = ObtainMotionEvent(event_time + kOneSecond, MotionEvent::ACTION_UP);
+  event = ObtainMotionEvent(event_time + kOneSecond, MotionEvent::Action::UP);
   gesture_provider_->OnTouchEvent(event);
   EXPECT_EQ(ET_GESTURE_LONG_TAP, GetMostRecentGestureEventType());
   EXPECT_EQ(1, GetMostRecentGestureEvent().details.touch_points());
@@ -1235,7 +1200,7 @@
   base::TimeTicks event_time = base::TimeTicks::Now();
 
   MockMotionEvent event =
-      ObtainMotionEvent(event_time, MotionEvent::ACTION_DOWN);
+      ObtainMotionEvent(event_time, MotionEvent::Action::DOWN);
   EXPECT_TRUE(gesture_provider_->OnTouchEvent(event));
 
   const base::TimeDelta long_press_timeout =
@@ -1245,8 +1210,7 @@
   EXPECT_EQ(ET_GESTURE_LONG_PRESS, GetMostRecentGestureEventType());
   EXPECT_EQ(1, GetMostRecentGestureEvent().details.touch_points());
   event = ObtainMotionEvent(event_time + long_press_timeout,
-                            MotionEvent::ACTION_MOVE,
-                            kFakeCoordX + 100,
+                            MotionEvent::Action::MOVE, kFakeCoordX + 100,
                             kFakeCoordY + 100);
   gesture_provider_->OnTouchEvent(event);
 
@@ -1255,7 +1219,7 @@
   EXPECT_TRUE(HasReceivedGesture(ET_GESTURE_SCROLL_BEGIN));
 
   event = ObtainMotionEvent(event_time + long_press_timeout,
-                            MotionEvent::ACTION_UP);
+                            MotionEvent::Action::UP);
   gesture_provider_->OnTouchEvent(event);
   EXPECT_FALSE(HasReceivedGesture(ET_GESTURE_LONG_TAP));
 }
@@ -1265,21 +1229,17 @@
   int motion_event_id = 6;
 
   MockMotionEvent event = ObtainMotionEvent(
-      event_time, MotionEvent::ACTION_DOWN, kFakeCoordX, kFakeCoordY);
+      event_time, MotionEvent::Action::DOWN, kFakeCoordX, kFakeCoordY);
   EXPECT_TRUE(gesture_provider_->OnTouchEvent(event));
 
   event = ObtainMotionEvent(event_time + kOneMicrosecond,
-                            MotionEvent::ACTION_UP,
-                            kFakeCoordX,
-                            kFakeCoordY);
+                            MotionEvent::Action::UP, kFakeCoordX, kFakeCoordY);
   gesture_provider_->OnTouchEvent(event);
   EXPECT_EQ(ET_GESTURE_TAP_UNCONFIRMED, GetMostRecentGestureEventType());
   EXPECT_EQ(1, GetMostRecentGestureEvent().details.touch_points());
 
   event_time += GetValidDoubleTapDelay();
-  event = ObtainMotionEvent(event_time,
-                            MotionEvent::ACTION_DOWN,
-                            kFakeCoordX,
+  event = ObtainMotionEvent(event_time, MotionEvent::Action::DOWN, kFakeCoordX,
                             kFakeCoordY);
   EXPECT_TRUE(gesture_provider_->OnTouchEvent(event));
   EXPECT_EQ(ET_GESTURE_TAP_DOWN, GetMostRecentGestureEventType());
@@ -1292,8 +1252,7 @@
   EXPECT_FALSE(HasReceivedGesture(ET_GESTURE_LONG_PRESS));
 
   event = ObtainMotionEvent(event_time + long_press_timeout,
-                            MotionEvent::ACTION_MOVE,
-                            kFakeCoordX + 20,
+                            MotionEvent::Action::MOVE, kFakeCoordX + 20,
                             kFakeCoordY + 20);
   event.SetPrimaryPointerId(motion_event_id);
 
@@ -1304,10 +1263,9 @@
   EXPECT_EQ(1, GetMostRecentGestureEvent().details.touch_points());
   EXPECT_TRUE(gesture_provider_->IsDoubleTapInProgress());
 
-  event = ObtainMotionEvent(event_time + long_press_timeout + kOneMicrosecond,
-                            MotionEvent::ACTION_UP,
-                            kFakeCoordX,
-                            kFakeCoordY + 1);
+  event =
+      ObtainMotionEvent(event_time + long_press_timeout + kOneMicrosecond,
+                        MotionEvent::Action::UP, kFakeCoordX, kFakeCoordY + 1);
   event.SetPrimaryPointerId(motion_event_id);
   EXPECT_TRUE(gesture_provider_->OnTouchEvent(event));
   EXPECT_EQ(ET_GESTURE_SCROLL_END, GetMostRecentGestureEventType());
@@ -1325,12 +1283,11 @@
   base::TimeTicks event_time = base::TimeTicks::Now();
 
   MockMotionEvent event =
-      ObtainMotionEvent(event_time, MotionEvent::ACTION_DOWN);
+      ObtainMotionEvent(event_time, MotionEvent::Action::DOWN);
   EXPECT_TRUE(gesture_provider_->OnTouchEvent(event));
 
   event = ObtainMotionEvent(event_time + kOneMicrosecond * 2,
-                            MotionEvent::ACTION_MOVE,
-                            kFakeCoordX,
+                            MotionEvent::Action::MOVE, kFakeCoordX,
                             kFakeCoordY + touch_slop + scroll_delta);
   EXPECT_TRUE(gesture_provider_->OnTouchEvent(event));
 
@@ -1351,42 +1308,36 @@
   base::TimeTicks event_time = base::TimeTicks::Now();
 
   MockMotionEvent event =
-      ObtainMotionEvent(event_time, MotionEvent::ACTION_DOWN);
+      ObtainMotionEvent(event_time, MotionEvent::Action::DOWN);
   EXPECT_TRUE(gesture_provider_->OnTouchEvent(event));
 
-  event = ObtainMotionEvent(event_time + kOneMicrosecond * 2,
-                            MotionEvent::ACTION_MOVE,
-                            kFakeCoordX + touch_slop_pixels / scale_factor,
-                            kFakeCoordY);
+  event = ObtainMotionEvent(
+      event_time + kOneMicrosecond * 2, MotionEvent::Action::MOVE,
+      kFakeCoordX + touch_slop_pixels / scale_factor, kFakeCoordY);
   EXPECT_TRUE(gesture_provider_->OnTouchEvent(event));
   EXPECT_FALSE(HasReceivedGesture(ET_GESTURE_SCROLL_BEGIN));
 
   event = ObtainMotionEvent(event_time + kOneMicrosecond * 2,
-                            MotionEvent::ACTION_MOVE,
-                            kFakeCoordX,
+                            MotionEvent::Action::MOVE, kFakeCoordX,
                             kFakeCoordY + touch_slop_pixels / scale_factor);
   EXPECT_TRUE(gesture_provider_->OnTouchEvent(event));
   EXPECT_FALSE(HasReceivedGesture(ET_GESTURE_SCROLL_BEGIN));
 
-  event = ObtainMotionEvent(event_time + kOneMicrosecond * 2,
-                            MotionEvent::ACTION_MOVE,
-                            kFakeCoordX - touch_slop_pixels / scale_factor,
-                            kFakeCoordY);
+  event = ObtainMotionEvent(
+      event_time + kOneMicrosecond * 2, MotionEvent::Action::MOVE,
+      kFakeCoordX - touch_slop_pixels / scale_factor, kFakeCoordY);
   EXPECT_TRUE(gesture_provider_->OnTouchEvent(event));
   EXPECT_FALSE(HasReceivedGesture(ET_GESTURE_SCROLL_BEGIN));
 
   event = ObtainMotionEvent(event_time + kOneMicrosecond * 2,
-                            MotionEvent::ACTION_MOVE,
-                            kFakeCoordX,
+                            MotionEvent::Action::MOVE, kFakeCoordX,
                             kFakeCoordY - touch_slop_pixels / scale_factor);
   EXPECT_TRUE(gesture_provider_->OnTouchEvent(event));
   EXPECT_FALSE(HasReceivedGesture(ET_GESTURE_SCROLL_BEGIN));
 
-  event =
-      ObtainMotionEvent(event_time + kOneMicrosecond * 2,
-                        MotionEvent::ACTION_MOVE,
-                        kFakeCoordX,
-                        kFakeCoordY + (touch_slop_pixels + 1.f) / scale_factor);
+  event = ObtainMotionEvent(
+      event_time + kOneMicrosecond * 2, MotionEvent::Action::MOVE, kFakeCoordX,
+      kFakeCoordY + (touch_slop_pixels + 1.f) / scale_factor);
   EXPECT_TRUE(gesture_provider_->OnTouchEvent(event));
   EXPECT_TRUE(HasReceivedGesture(ET_GESTURE_SCROLL_BEGIN));
 }
@@ -1395,16 +1346,14 @@
   base::TimeTicks event_time = base::TimeTicks::Now();
 
   MockMotionEvent event =
-      ObtainMotionEvent(event_time, MotionEvent::ACTION_DOWN);
+      ObtainMotionEvent(event_time, MotionEvent::Action::DOWN);
   EXPECT_TRUE(gesture_provider_->OnTouchEvent(event));
 
   EXPECT_EQ(ET_GESTURE_TAP_DOWN, GetMostRecentGestureEventType());
   EXPECT_EQ(1, GetMostRecentGestureEvent().details.touch_points());
 
   event = ObtainMotionEvent(event_time + kOneMicrosecond,
-                            MotionEvent::ACTION_UP,
-                            kFakeCoordX,
-                            kFakeCoordY);
+                            MotionEvent::Action::UP, kFakeCoordX, kFakeCoordY);
   gesture_provider_->OnTouchEvent(event);
   EXPECT_EQ(ET_GESTURE_TAP_UNCONFIRMED, GetMostRecentGestureEventType());
   EXPECT_EQ(1, GetMostRecentGestureEvent().details.touch_points());
@@ -1412,18 +1361,14 @@
   // If the second tap follows the first in too short a time span, no double-tap
   // will occur.
   event_time += (GetDoubleTapMinTime() / 2);
-  event = ObtainMotionEvent(event_time,
-                            MotionEvent::ACTION_DOWN,
-                            kFakeCoordX,
+  event = ObtainMotionEvent(event_time, MotionEvent::Action::DOWN, kFakeCoordX,
                             kFakeCoordY);
   EXPECT_TRUE(gesture_provider_->OnTouchEvent(event));
   EXPECT_EQ(ET_GESTURE_TAP_DOWN, GetMostRecentGestureEventType());
   EXPECT_EQ(1, GetMostRecentGestureEvent().details.touch_points());
 
   event = ObtainMotionEvent(event_time + kOneMicrosecond,
-                            MotionEvent::ACTION_UP,
-                            kFakeCoordX,
-                            kFakeCoordY);
+                            MotionEvent::Action::UP, kFakeCoordX, kFakeCoordY);
   EXPECT_TRUE(gesture_provider_->OnTouchEvent(event));
   EXPECT_EQ(ET_GESTURE_TAP_UNCONFIRMED, GetMostRecentGestureEventType());
 }
@@ -1434,29 +1379,23 @@
 
   base::TimeTicks event_time = base::TimeTicks::Now();
   MockMotionEvent event = ObtainMotionEvent(
-      event_time, MotionEvent::ACTION_DOWN, kFakeCoordX, kFakeCoordY);
+      event_time, MotionEvent::Action::DOWN, kFakeCoordX, kFakeCoordY);
   EXPECT_TRUE(gesture_provider_->OnTouchEvent(event));
   EXPECT_EQ(ET_GESTURE_TAP_DOWN, GetMostRecentGestureEventType());
 
   event = ObtainMotionEvent(event_time + kOneMicrosecond,
-                            MotionEvent::ACTION_UP,
-                            kFakeCoordX,
-                            kFakeCoordY);
+                            MotionEvent::Action::UP, kFakeCoordX, kFakeCoordY);
   EXPECT_TRUE(gesture_provider_->OnTouchEvent(event));
   EXPECT_EQ(ET_GESTURE_TAP, GetMostRecentGestureEventType());
 
   event_time += GetValidDoubleTapDelay();
-  event = ObtainMotionEvent(event_time,
-                            MotionEvent::ACTION_DOWN,
-                            kFakeCoordX,
+  event = ObtainMotionEvent(event_time, MotionEvent::Action::DOWN, kFakeCoordX,
                             kFakeCoordY);
   EXPECT_TRUE(gesture_provider_->OnTouchEvent(event));
   EXPECT_EQ(ET_GESTURE_TAP_DOWN, GetMostRecentGestureEventType());
 
   event = ObtainMotionEvent(event_time + kOneMicrosecond,
-                            MotionEvent::ACTION_UP,
-                            kFakeCoordX,
-                            kFakeCoordY);
+                            MotionEvent::Action::UP, kFakeCoordX, kFakeCoordY);
   EXPECT_TRUE(gesture_provider_->OnTouchEvent(event));
   EXPECT_EQ(ET_GESTURE_TAP, GetMostRecentGestureEventType());
 
@@ -1464,15 +1403,13 @@
   gesture_provider_->SetDoubleTapSupportForPlatformEnabled(true);
 
   event_time = base::TimeTicks::Now();
-  event = ObtainMotionEvent(
-      event_time, MotionEvent::ACTION_DOWN, kFakeCoordX, kFakeCoordY);
+  event = ObtainMotionEvent(event_time, MotionEvent::Action::DOWN, kFakeCoordX,
+                            kFakeCoordY);
   EXPECT_TRUE(gesture_provider_->OnTouchEvent(event));
   EXPECT_EQ(5U, GetReceivedGestureCount());
 
   event = ObtainMotionEvent(event_time + kOneMicrosecond,
-                            MotionEvent::ACTION_UP,
-                            kFakeCoordX,
-                            kFakeCoordY);
+                            MotionEvent::Action::UP, kFakeCoordX, kFakeCoordY);
   EXPECT_TRUE(gesture_provider_->OnTouchEvent(event));
   EXPECT_EQ(ET_GESTURE_TAP_UNCONFIRMED, GetMostRecentGestureEventType());
 
@@ -1483,32 +1420,24 @@
   gesture_provider_->SetDoubleTapSupportForPlatformEnabled(true);
 
   event_time += GetValidDoubleTapDelay();
-  event = ObtainMotionEvent(event_time,
-                            MotionEvent::ACTION_DOWN,
-                            kFakeCoordX,
+  event = ObtainMotionEvent(event_time, MotionEvent::Action::DOWN, kFakeCoordX,
                             kFakeCoordY);
   EXPECT_TRUE(gesture_provider_->OnTouchEvent(event));
   EXPECT_EQ(ET_GESTURE_TAP_DOWN, GetMostRecentGestureEventType());
 
   event = ObtainMotionEvent(event_time + kOneMicrosecond,
-                            MotionEvent::ACTION_UP,
-                            kFakeCoordX,
-                            kFakeCoordY);
+                            MotionEvent::Action::UP, kFakeCoordX, kFakeCoordY);
   EXPECT_TRUE(gesture_provider_->OnTouchEvent(event));
   EXPECT_EQ(ET_GESTURE_TAP_UNCONFIRMED, GetMostRecentGestureEventType());
 
   event_time += GetValidDoubleTapDelay();
-  event = ObtainMotionEvent(event_time,
-                            MotionEvent::ACTION_DOWN,
-                            kFakeCoordX,
+  event = ObtainMotionEvent(event_time, MotionEvent::Action::DOWN, kFakeCoordX,
                             kFakeCoordY);
   EXPECT_TRUE(gesture_provider_->OnTouchEvent(event));
   EXPECT_EQ(ET_GESTURE_TAP_DOWN, GetMostRecentGestureEventType());
 
   event = ObtainMotionEvent(event_time + kOneMicrosecond,
-                            MotionEvent::ACTION_UP,
-                            kFakeCoordX,
-                            kFakeCoordY);
+                            MotionEvent::Action::UP, kFakeCoordX, kFakeCoordY);
   EXPECT_TRUE(gesture_provider_->OnTouchEvent(event));
   EXPECT_EQ(ET_GESTURE_DOUBLE_TAP, GetMostRecentGestureEventType());
 }
@@ -1518,15 +1447,13 @@
 
   base::TimeTicks event_time = base::TimeTicks::Now();
   MockMotionEvent event = ObtainMotionEvent(
-      event_time, MotionEvent::ACTION_DOWN, kFakeCoordX, kFakeCoordY);
+      event_time, MotionEvent::Action::DOWN, kFakeCoordX, kFakeCoordY);
   EXPECT_TRUE(gesture_provider_->OnTouchEvent(event));
   EXPECT_EQ(ET_GESTURE_TAP_DOWN, GetMostRecentGestureEventType());
   EXPECT_EQ(1U, GetReceivedGestureCount());
 
   event = ObtainMotionEvent(event_time + kOneMicrosecond,
-                            MotionEvent::ACTION_UP,
-                            kFakeCoordX,
-                            kFakeCoordY);
+                            MotionEvent::Action::UP, kFakeCoordX, kFakeCoordY);
   EXPECT_TRUE(gesture_provider_->OnTouchEvent(event));
   EXPECT_EQ(ET_GESTURE_TAP_UNCONFIRMED, GetMostRecentGestureEventType());
   EXPECT_EQ(2U, GetReceivedGestureCount());
@@ -1550,22 +1477,19 @@
   gesture_provider_->SetDoubleTapSupportForPlatformEnabled(false);
 
   MockMotionEvent event =
-      ObtainMotionEvent(down_time_1, MotionEvent::ACTION_DOWN);
+      ObtainMotionEvent(down_time_1, MotionEvent::Action::DOWN);
   EXPECT_TRUE(gesture_provider_->OnTouchEvent(event));
 
   event = ObtainMotionEvent(down_time_1 + kOneMicrosecond,
-                            MotionEvent::ACTION_UP,
-                            kFakeCoordX,
-                            kFakeCoordY);
+                            MotionEvent::Action::UP, kFakeCoordX, kFakeCoordY);
   gesture_provider_->OnTouchEvent(event);
 
-  event = ObtainMotionEvent(
-      down_time_2, MotionEvent::ACTION_DOWN, kFakeCoordX, kFakeCoordY);
+  event = ObtainMotionEvent(down_time_2, MotionEvent::Action::DOWN, kFakeCoordX,
+                            kFakeCoordY);
   EXPECT_TRUE(gesture_provider_->OnTouchEvent(event));
 
   event = ObtainMotionEvent(down_time_2 + kOneMicrosecond,
-                            MotionEvent::ACTION_MOVE,
-                            kFakeCoordX,
+                            MotionEvent::Action::MOVE, kFakeCoordX,
                             kFakeCoordY + 100);
 
   // The move should become a scroll, as doubletap drag zoom is disabled.
@@ -1574,8 +1498,7 @@
   EXPECT_FALSE(HasReceivedGesture(ET_GESTURE_PINCH_BEGIN));
 
   event = ObtainMotionEvent(down_time_2 + kOneMicrosecond * 2,
-                            MotionEvent::ACTION_MOVE,
-                            kFakeCoordX,
+                            MotionEvent::Action::MOVE, kFakeCoordX,
                             kFakeCoordY + 200);
   EXPECT_TRUE(gesture_provider_->OnTouchEvent(event));
   EXPECT_EQ(ET_GESTURE_SCROLL_UPDATE, GetMostRecentGestureEventType());
@@ -1585,8 +1508,7 @@
   EXPECT_FALSE(HasReceivedGesture(ET_GESTURE_PINCH_UPDATE));
 
   event = ObtainMotionEvent(down_time_2 + kOneMicrosecond * 3,
-                            MotionEvent::ACTION_UP,
-                            kFakeCoordX,
+                            MotionEvent::Action::UP, kFakeCoordX,
                             kFakeCoordY + 200);
   EXPECT_TRUE(gesture_provider_->OnTouchEvent(event));
   EXPECT_FALSE(HasReceivedGesture(ET_GESTURE_PINCH_END));
@@ -1602,22 +1524,19 @@
   gesture_provider_->SetDoubleTapSupportForPageEnabled(false);
 
   MockMotionEvent event =
-      ObtainMotionEvent(down_time_1, MotionEvent::ACTION_DOWN);
+      ObtainMotionEvent(down_time_1, MotionEvent::Action::DOWN);
   EXPECT_TRUE(gesture_provider_->OnTouchEvent(event));
 
   event = ObtainMotionEvent(down_time_1 + kOneMicrosecond,
-                            MotionEvent::ACTION_UP,
-                            kFakeCoordX,
-                            kFakeCoordY);
+                            MotionEvent::Action::UP, kFakeCoordX, kFakeCoordY);
   gesture_provider_->OnTouchEvent(event);
 
-  event = ObtainMotionEvent(
-      down_time_2, MotionEvent::ACTION_DOWN, kFakeCoordX, kFakeCoordY);
+  event = ObtainMotionEvent(down_time_2, MotionEvent::Action::DOWN, kFakeCoordX,
+                            kFakeCoordY);
   EXPECT_TRUE(gesture_provider_->OnTouchEvent(event));
 
   event = ObtainMotionEvent(down_time_2 + kOneMicrosecond,
-                            MotionEvent::ACTION_MOVE,
-                            kFakeCoordX,
+                            MotionEvent::Action::MOVE, kFakeCoordX,
                             kFakeCoordY + 100);
 
   // The move should become a scroll, as double tap drag zoom is disabled.
@@ -1626,8 +1545,7 @@
   EXPECT_FALSE(HasReceivedGesture(ET_GESTURE_PINCH_BEGIN));
 
   event = ObtainMotionEvent(down_time_2 + kOneMicrosecond * 2,
-                            MotionEvent::ACTION_MOVE,
-                            kFakeCoordX,
+                            MotionEvent::Action::MOVE, kFakeCoordX,
                             kFakeCoordY + 200);
   EXPECT_TRUE(gesture_provider_->OnTouchEvent(event));
   EXPECT_EQ(ET_GESTURE_SCROLL_UPDATE, GetMostRecentGestureEventType());
@@ -1635,8 +1553,7 @@
   EXPECT_FALSE(HasReceivedGesture(ET_GESTURE_PINCH_UPDATE));
 
   event = ObtainMotionEvent(down_time_2 + kOneMicrosecond * 3,
-                            MotionEvent::ACTION_UP,
-                            kFakeCoordX,
+                            MotionEvent::Action::UP, kFakeCoordX,
                             kFakeCoordY + 200);
   EXPECT_TRUE(gesture_provider_->OnTouchEvent(event));
   EXPECT_FALSE(HasReceivedGesture(ET_GESTURE_PINCH_END));
@@ -1653,19 +1570,16 @@
 
   // Start a double-tap drag gesture.
   MockMotionEvent event =
-      ObtainMotionEvent(down_time_1, MotionEvent::ACTION_DOWN);
+      ObtainMotionEvent(down_time_1, MotionEvent::Action::DOWN);
   EXPECT_TRUE(gesture_provider_->OnTouchEvent(event));
   event = ObtainMotionEvent(down_time_1 + kOneMicrosecond,
-                            MotionEvent::ACTION_UP,
-                            kFakeCoordX,
-                            kFakeCoordY);
+                            MotionEvent::Action::UP, kFakeCoordX, kFakeCoordY);
   gesture_provider_->OnTouchEvent(event);
-  event = ObtainMotionEvent(
-      down_time_2, MotionEvent::ACTION_DOWN, kFakeCoordX, kFakeCoordY);
+  event = ObtainMotionEvent(down_time_2, MotionEvent::Action::DOWN, kFakeCoordX,
+                            kFakeCoordY);
   EXPECT_TRUE(gesture_provider_->OnTouchEvent(event));
   event = ObtainMotionEvent(down_time_2 + kOneMicrosecond,
-                            MotionEvent::ACTION_MOVE,
-                            kFakeCoordX,
+                            MotionEvent::Action::MOVE, kFakeCoordX,
                             kFakeCoordY + 100);
   EXPECT_TRUE(gesture_provider_->OnTouchEvent(event));
   EXPECT_TRUE(HasReceivedGesture(ET_GESTURE_SCROLL_BEGIN));
@@ -1680,16 +1594,14 @@
 
   // Double tap zoom updates should continue.
   event = ObtainMotionEvent(down_time_2 + kOneMicrosecond * 2,
-                            MotionEvent::ACTION_MOVE,
-                            kFakeCoordX,
+                            MotionEvent::Action::MOVE, kFakeCoordX,
                             kFakeCoordY + 200);
   EXPECT_TRUE(gesture_provider_->OnTouchEvent(event));
   EXPECT_EQ(ET_GESTURE_PINCH_UPDATE, GetMostRecentGestureEventType());
   EXPECT_EQ(1, GetMostRecentGestureEvent().details.touch_points());
   EXPECT_LT(1.f, GetMostRecentGestureEvent().details.scale());
   event = ObtainMotionEvent(down_time_2 + kOneMicrosecond * 3,
-                            MotionEvent::ACTION_UP,
-                            kFakeCoordX,
+                            MotionEvent::Action::UP, kFakeCoordX,
                             kFakeCoordY + 200);
   EXPECT_TRUE(gesture_provider_->OnTouchEvent(event));
   EXPECT_TRUE(HasReceivedGesture(ET_GESTURE_PINCH_END));
@@ -1703,19 +1615,16 @@
   down_time_2 += kOneMicrosecond * 40;
 
   // Start a double-tap drag gesture.
-  event = ObtainMotionEvent(down_time_1, MotionEvent::ACTION_DOWN);
+  event = ObtainMotionEvent(down_time_1, MotionEvent::Action::DOWN);
   EXPECT_TRUE(gesture_provider_->OnTouchEvent(event));
   event = ObtainMotionEvent(down_time_1 + kOneMicrosecond,
-                            MotionEvent::ACTION_UP,
-                            kFakeCoordX,
-                            kFakeCoordY);
+                            MotionEvent::Action::UP, kFakeCoordX, kFakeCoordY);
   gesture_provider_->OnTouchEvent(event);
-  event = ObtainMotionEvent(
-      down_time_2, MotionEvent::ACTION_DOWN, kFakeCoordX, kFakeCoordY);
+  event = ObtainMotionEvent(down_time_2, MotionEvent::Action::DOWN, kFakeCoordX,
+                            kFakeCoordY);
   EXPECT_TRUE(gesture_provider_->OnTouchEvent(event));
   event = ObtainMotionEvent(down_time_2 + kOneMicrosecond,
-                            MotionEvent::ACTION_MOVE,
-                            kFakeCoordX,
+                            MotionEvent::Action::MOVE, kFakeCoordX,
                             kFakeCoordY + 100);
   EXPECT_TRUE(gesture_provider_->OnTouchEvent(event));
   EXPECT_TRUE(HasReceivedGesture(ET_GESTURE_SCROLL_BEGIN));
@@ -1724,15 +1633,13 @@
   // Double tap zoom updates should not be sent.
   // Instead, the second tap drag becomes a scroll gesture sequence.
   event = ObtainMotionEvent(down_time_2 + kOneMicrosecond * 2,
-                            MotionEvent::ACTION_MOVE,
-                            kFakeCoordX,
+                            MotionEvent::Action::MOVE, kFakeCoordX,
                             kFakeCoordY + 200);
   EXPECT_TRUE(gesture_provider_->OnTouchEvent(event));
   EXPECT_TRUE(HasReceivedGesture(ET_GESTURE_SCROLL_UPDATE));
   EXPECT_FALSE(HasReceivedGesture(ET_GESTURE_PINCH_UPDATE));
   event = ObtainMotionEvent(down_time_2 + kOneMicrosecond * 3,
-                            MotionEvent::ACTION_UP,
-                            kFakeCoordX,
+                            MotionEvent::Action::UP, kFakeCoordX,
                             kFakeCoordY + 200);
   EXPECT_TRUE(gesture_provider_->OnTouchEvent(event));
   EXPECT_FALSE(HasReceivedGesture(ET_GESTURE_PINCH_END));
@@ -1754,7 +1661,7 @@
   int secondary_coord_y = kFakeCoordY + 20 * touch_slop;
 
   MockMotionEvent event =
-      ObtainMotionEvent(event_time, MotionEvent::ACTION_DOWN);
+      ObtainMotionEvent(event_time, MotionEvent::Action::DOWN);
   event.SetPrimaryPointerId(motion_event_id);
   event.SetRawOffset(raw_offset_x, raw_offset_y);
   EXPECT_TRUE(gesture_provider_->OnTouchEvent(event));
@@ -1770,11 +1677,8 @@
   // Toggling double-tap support should not take effect until the next sequence.
   gesture_provider_->SetDoubleTapSupportForPageEnabled(true);
 
-  event = ObtainMotionEvent(event_time,
-                            MotionEvent::ACTION_POINTER_DOWN,
-                            kFakeCoordX,
-                            kFakeCoordY,
-                            secondary_coord_x,
+  event = ObtainMotionEvent(event_time, MotionEvent::Action::POINTER_DOWN,
+                            kFakeCoordX, kFakeCoordY, secondary_coord_x,
                             secondary_coord_y);
   event.SetPrimaryPointerId(motion_event_id);
   event.SetRawOffset(raw_offset_x, raw_offset_y);
@@ -1787,12 +1691,8 @@
 
   secondary_coord_x += 5 * touch_slop;
   secondary_coord_y += 5 * touch_slop;
-  event = ObtainMotionEvent(event_time,
-                            MotionEvent::ACTION_MOVE,
-                            kFakeCoordX,
-                            kFakeCoordY,
-                            secondary_coord_x,
-                            secondary_coord_y);
+  event = ObtainMotionEvent(event_time, MotionEvent::Action::MOVE, kFakeCoordX,
+                            kFakeCoordY, secondary_coord_x, secondary_coord_y);
   event.SetPrimaryPointerId(motion_event_id);
   event.SetRawOffset(raw_offset_x, raw_offset_y);
 
@@ -1821,12 +1721,8 @@
 
   secondary_coord_x += 2 * touch_slop;
   secondary_coord_y += 2 * touch_slop;
-  event = ObtainMotionEvent(event_time,
-                            MotionEvent::ACTION_MOVE,
-                            kFakeCoordX,
-                            kFakeCoordY,
-                            secondary_coord_x,
-                            secondary_coord_y);
+  event = ObtainMotionEvent(event_time, MotionEvent::Action::MOVE, kFakeCoordX,
+                            kFakeCoordY, secondary_coord_x, secondary_coord_y);
   event.SetPrimaryPointerId(motion_event_id);
 
   // Toggling double-tap support should not take effect until the next sequence.
@@ -1844,11 +1740,8 @@
                  secondary_coord_y - kFakeCoordY + kMockTouchRadius * 2),
       GetMostRecentGestureEvent().details.bounding_box_f());
 
-  event = ObtainMotionEvent(event_time,
-                            MotionEvent::ACTION_POINTER_UP,
-                            kFakeCoordX,
-                            kFakeCoordY,
-                            secondary_coord_x,
+  event = ObtainMotionEvent(event_time, MotionEvent::Action::POINTER_UP,
+                            kFakeCoordX, kFakeCoordY, secondary_coord_x,
                             secondary_coord_y);
   event.SetPrimaryPointerId(motion_event_id);
 
@@ -1863,7 +1756,7 @@
                  secondary_coord_y - kFakeCoordY + kMockTouchRadius * 2),
       GetMostRecentGestureEvent().details.bounding_box_f());
 
-  event = ObtainMotionEvent(event_time, MotionEvent::ACTION_UP);
+  event = ObtainMotionEvent(event_time, MotionEvent::Action::UP);
   gesture_provider_->OnTouchEvent(event);
   EXPECT_EQ(ET_GESTURE_SCROLL_END, GetMostRecentGestureEventType());
   EXPECT_EQ(1, GetMostRecentGestureEvent().details.touch_points());
@@ -1883,49 +1776,45 @@
   gesture_provider_->SetMultiTouchZoomSupportEnabled(true);
 
   MockMotionEvent event =
-      ObtainMotionEvent(event_time, MotionEvent::ACTION_DOWN);
+      ObtainMotionEvent(event_time, MotionEvent::Action::DOWN);
   EXPECT_TRUE(gesture_provider_->OnTouchEvent(event));
   EXPECT_EQ(ET_GESTURE_TAP_DOWN, GetMostRecentGestureEventType());
   EXPECT_EQ(1U, GetReceivedGestureCount());
 
-  event = ObtainMotionEvent(event_time + kOneSecond,
-                            MotionEvent::ACTION_MOVE);
+  event = ObtainMotionEvent(event_time + kOneSecond, MotionEvent::Action::MOVE);
   event.SetTouchMajor(0.1f);
   EXPECT_TRUE(gesture_provider_->OnTouchEvent(event));
   EXPECT_EQ(1U, GetReceivedGestureCount());
 
-  event = ObtainMotionEvent(event_time + kOneSecond * 2,
-                            MotionEvent::ACTION_MOVE,
-                            kFakeCoordX + 1.f,
-                            kFakeCoordY);
+  event =
+      ObtainMotionEvent(event_time + kOneSecond * 2, MotionEvent::Action::MOVE,
+                        kFakeCoordX + 1.f, kFakeCoordY);
   event.SetTouchMajor(1.f);
   EXPECT_TRUE(gesture_provider_->OnTouchEvent(event));
   EXPECT_EQ(1U, GetReceivedGestureCount());
 
-  event = ObtainMotionEvent(event_time + kOneSecond * 3,
-                            MotionEvent::ACTION_MOVE);
+  event =
+      ObtainMotionEvent(event_time + kOneSecond * 3, MotionEvent::Action::MOVE);
   event.SetTouchMajor(kFatFingerSize * 3.5f);
   EXPECT_TRUE(gesture_provider_->OnTouchEvent(event));
   EXPECT_EQ(1U, GetReceivedGestureCount());
 
-  event = ObtainMotionEvent(event_time + kOneSecond * 4,
-                            MotionEvent::ACTION_MOVE);
+  event =
+      ObtainMotionEvent(event_time + kOneSecond * 4, MotionEvent::Action::MOVE);
   event.SetTouchMajor(kFatFingerSize * 5.f);
   EXPECT_TRUE(gesture_provider_->OnTouchEvent(event));
   EXPECT_EQ(1U, GetReceivedGestureCount());
 
-  event = ObtainMotionEvent(event_time + kOneSecond * 4,
-                            MotionEvent::ACTION_MOVE,
-                            kFakeCoordX + 50.f,
-                            kFakeCoordY - 25.f);
+  event =
+      ObtainMotionEvent(event_time + kOneSecond * 4, MotionEvent::Action::MOVE,
+                        kFakeCoordX + 50.f, kFakeCoordY - 25.f);
   event.SetTouchMajor(kFatFingerSize * 10.f);
   EXPECT_TRUE(gesture_provider_->OnTouchEvent(event));
   EXPECT_FALSE(HasReceivedGesture(ET_GESTURE_PINCH_BEGIN));
 
-  event = ObtainMotionEvent(event_time + kOneSecond * 4,
-                            MotionEvent::ACTION_MOVE,
-                            kFakeCoordX + 100.f,
-                            kFakeCoordY - 50.f);
+  event =
+      ObtainMotionEvent(event_time + kOneSecond * 4, MotionEvent::Action::MOVE,
+                        kFakeCoordX + 100.f, kFakeCoordY - 50.f);
   event.SetTouchMajor(kFatFingerSize * 5.f);
   EXPECT_TRUE(gesture_provider_->OnTouchEvent(event));
   EXPECT_FALSE(HasReceivedGesture(ET_GESTURE_PINCH_BEGIN));
@@ -2065,7 +1954,7 @@
   base::TimeTicks event_time = base::TimeTicks::Now();
 
   MockMotionEvent event =
-      ObtainMotionEvent(event_time, MotionEvent::ACTION_DOWN);
+      ObtainMotionEvent(event_time, MotionEvent::Action::DOWN);
   EXPECT_TRUE(gesture_provider_->OnTouchEvent(event));
 
   const base::TimeDelta long_press_timeout =
@@ -2078,7 +1967,7 @@
   EXPECT_FALSE(HasDownEvent());
 
   event = ObtainMotionEvent(event_time + long_press_timeout,
-                            MotionEvent::ACTION_UP);
+                            MotionEvent::Action::UP);
   gesture_provider_->OnTouchEvent(event);
   EXPECT_FALSE(HasReceivedGesture(ET_GESTURE_LONG_TAP));
 }
@@ -2093,7 +1982,7 @@
   EXPECT_EQ(0U, GetReceivedGestureCount());
 
   MockMotionEvent event =
-      ObtainMotionEvent(event_time, MotionEvent::ACTION_DOWN);
+      ObtainMotionEvent(event_time, MotionEvent::Action::DOWN);
   event.SetPrimaryPointerId(motion_event_id);
   EXPECT_TRUE(gesture_provider_->OnTouchEvent(event));
   EXPECT_EQ(ET_GESTURE_TAP_DOWN, GetMostRecentGestureEventType());
@@ -2103,17 +1992,17 @@
   ASSERT_TRUE(CancelActiveTouchSequence());
   EXPECT_FALSE(HasDownEvent());
 
-  // Subsequent MotionEvent's are dropped until ACTION_DOWN.
+  // Subsequent MotionEvent's are dropped until Action::DOWN.
   event = ObtainMotionEvent(event_time + kOneMicrosecond,
-                            MotionEvent::ACTION_MOVE);
+                            MotionEvent::Action::MOVE);
   EXPECT_FALSE(gesture_provider_->OnTouchEvent(event));
 
   event = ObtainMotionEvent(event_time + kOneMicrosecond * 2,
-                            MotionEvent::ACTION_UP);
+                            MotionEvent::Action::UP);
   EXPECT_FALSE(gesture_provider_->OnTouchEvent(event));
 
   event = ObtainMotionEvent(event_time + kOneMicrosecond * 3,
-                            MotionEvent::ACTION_DOWN);
+                            MotionEvent::Action::DOWN);
   EXPECT_TRUE(gesture_provider_->OnTouchEvent(event));
   EXPECT_EQ(ET_GESTURE_TAP_DOWN, GetMostRecentGestureEventType());
   EXPECT_EQ(1, GetMostRecentGestureEvent().details.touch_points());
@@ -2126,25 +2015,24 @@
   gesture_provider_->SetDoubleTapSupportForPlatformEnabled(true);
 
   MockMotionEvent event =
-      ObtainMotionEvent(down_time_1, MotionEvent::ACTION_DOWN);
+      ObtainMotionEvent(down_time_1, MotionEvent::Action::DOWN);
   EXPECT_TRUE(gesture_provider_->OnTouchEvent(event));
   EXPECT_EQ(ET_GESTURE_TAP_DOWN, GetMostRecentGestureEventType());
   EXPECT_EQ(1, GetMostRecentGestureEvent().details.touch_points());
 
   event =
-      ObtainMotionEvent(down_time_1 + kOneMicrosecond, MotionEvent::ACTION_UP);
+      ObtainMotionEvent(down_time_1 + kOneMicrosecond, MotionEvent::Action::UP);
   gesture_provider_->OnTouchEvent(event);
   EXPECT_EQ(ET_GESTURE_TAP_UNCONFIRMED, GetMostRecentGestureEventType());
   EXPECT_EQ(1, GetMostRecentGestureEvent().details.touch_points());
 
-  event = ObtainMotionEvent(down_time_2, MotionEvent::ACTION_DOWN);
+  event = ObtainMotionEvent(down_time_2, MotionEvent::Action::DOWN);
   EXPECT_TRUE(gesture_provider_->OnTouchEvent(event));
   EXPECT_EQ(ET_GESTURE_TAP_DOWN, GetMostRecentGestureEventType());
   EXPECT_EQ(1, GetMostRecentGestureEvent().details.touch_points());
 
   event = ObtainMotionEvent(down_time_2 + kOneMicrosecond,
-                            MotionEvent::ACTION_MOVE,
-                            kFakeCoordX,
+                            MotionEvent::Action::MOVE, kFakeCoordX,
                             kFakeCoordY - 30);
   EXPECT_TRUE(gesture_provider_->OnTouchEvent(event));
   EXPECT_TRUE(HasReceivedGesture(ET_GESTURE_SCROLL_BEGIN));
@@ -2152,28 +2040,21 @@
   EXPECT_EQ(ET_GESTURE_PINCH_UPDATE, GetMostRecentGestureEventType());
   EXPECT_EQ(1, GetMostRecentGestureEvent().details.touch_points());
 
-  event = ObtainMotionEvent(down_time_2 + kOneMicrosecond * 2,
-                            MotionEvent::ACTION_POINTER_DOWN,
-                            kFakeCoordX,
-                            kFakeCoordY - 30,
-                            kFakeCoordX + 50,
-                            kFakeCoordY + 50);
+  event = ObtainMotionEvent(
+      down_time_2 + kOneMicrosecond * 2, MotionEvent::Action::POINTER_DOWN,
+      kFakeCoordX, kFakeCoordY - 30, kFakeCoordX + 50, kFakeCoordY + 50);
   EXPECT_TRUE(gesture_provider_->OnTouchEvent(event));
   EXPECT_EQ(ET_GESTURE_PINCH_END, GetMostRecentGestureEventType());
   EXPECT_EQ(2, GetMostRecentGestureEvent().details.touch_points());
 
   const size_t gesture_count = GetReceivedGestureCount();
-  event = ObtainMotionEvent(down_time_2 + kOneMicrosecond * 3,
-                            MotionEvent::ACTION_POINTER_UP,
-                            kFakeCoordX,
-                            kFakeCoordY - 30,
-                            kFakeCoordX + 50,
-                            kFakeCoordY + 50);
+  event = ObtainMotionEvent(
+      down_time_2 + kOneMicrosecond * 3, MotionEvent::Action::POINTER_UP,
+      kFakeCoordX, kFakeCoordY - 30, kFakeCoordX + 50, kFakeCoordY + 50);
   EXPECT_TRUE(gesture_provider_->OnTouchEvent(event));
   EXPECT_EQ(gesture_count, GetReceivedGestureCount());
 
-  event = ObtainMotionEvent(down_time_2 + kOneSecond,
-                            MotionEvent::ACTION_UP);
+  event = ObtainMotionEvent(down_time_2 + kOneSecond, MotionEvent::Action::UP);
   EXPECT_TRUE(gesture_provider_->OnTouchEvent(event));
   EXPECT_EQ(gesture_count + 1, GetReceivedGestureCount());
   EXPECT_EQ(ET_GESTURE_SCROLL_END, GetMostRecentGestureEventType());
@@ -2189,7 +2070,7 @@
 
   EXPECT_EQ(0U, GetReceivedGestureCount());
   MockMotionEvent event =
-      ObtainMotionEvent(event_time, MotionEvent::ACTION_DOWN, 1, 1);
+      ObtainMotionEvent(event_time, MotionEvent::Action::DOWN, 1, 1);
   event.SetRawOffset(raw_offset_x, raw_offset_y);
   EXPECT_TRUE(gesture_provider_->OnTouchEvent(event));
   EXPECT_EQ(ET_GESTURE_BEGIN, GetReceivedGesture(0).type());
@@ -2204,8 +2085,8 @@
                        kMockTouchRadius * 2, kMockTouchRadius * 2),
             GetMostRecentGestureEvent().details.bounding_box_f());
 
-  event = ObtainMotionEvent(
-      event_time, MotionEvent::ACTION_POINTER_DOWN, 1, 1, 2, 2);
+  event = ObtainMotionEvent(event_time, MotionEvent::Action::POINTER_DOWN, 1, 1,
+                            2, 2);
   event.SetRawOffset(raw_offset_x, raw_offset_y);
   EXPECT_TRUE(gesture_provider_->OnTouchEvent(event));
   EXPECT_EQ(ET_GESTURE_BEGIN, GetMostRecentGestureEventType());
@@ -2216,8 +2097,8 @@
   EXPECT_EQ(2 + raw_offset_x, GetMostRecentGestureEvent().raw_x);
   EXPECT_EQ(2 + raw_offset_y, GetMostRecentGestureEvent().raw_y);
 
-  event = ObtainMotionEvent(
-      event_time, MotionEvent::ACTION_POINTER_DOWN, 1, 1, 2, 2, 3, 3);
+  event = ObtainMotionEvent(event_time, MotionEvent::Action::POINTER_DOWN, 1, 1,
+                            2, 2, 3, 3);
   event.SetRawOffset(raw_offset_x, raw_offset_y);
   EXPECT_TRUE(gesture_provider_->OnTouchEvent(event));
   EXPECT_EQ(ET_GESTURE_BEGIN, GetMostRecentGestureEventType());
@@ -2228,8 +2109,8 @@
   EXPECT_EQ(3 + raw_offset_x, GetMostRecentGestureEvent().raw_x);
   EXPECT_EQ(3 + raw_offset_y, GetMostRecentGestureEvent().raw_y);
 
-  event = ObtainMotionEvent(
-      event_time, MotionEvent::ACTION_POINTER_UP, 1, 1, 2, 2, 3, 3);
+  event = ObtainMotionEvent(event_time, MotionEvent::Action::POINTER_UP, 1, 1,
+                            2, 2, 3, 3);
   event.SetRawOffset(raw_offset_x, raw_offset_y);
   EXPECT_TRUE(gesture_provider_->OnTouchEvent(event));
   EXPECT_EQ(ET_GESTURE_END, GetMostRecentGestureEventType());
@@ -2240,8 +2121,8 @@
   EXPECT_EQ(1 + raw_offset_x, GetMostRecentGestureEvent().raw_x);
   EXPECT_EQ(1 + raw_offset_y, GetMostRecentGestureEvent().raw_y);
 
-  event = ObtainMotionEvent(
-      event_time, MotionEvent::ACTION_POINTER_DOWN, 2, 2, 3, 3, 4, 4);
+  event = ObtainMotionEvent(event_time, MotionEvent::Action::POINTER_DOWN, 2, 2,
+                            3, 3, 4, 4);
   event.SetRawOffset(raw_offset_x, raw_offset_y);
   EXPECT_TRUE(gesture_provider_->OnTouchEvent(event));
   EXPECT_EQ(ET_GESTURE_BEGIN, GetMostRecentGestureEventType());
@@ -2252,8 +2133,8 @@
   EXPECT_EQ(4 + raw_offset_x, GetMostRecentGestureEvent().raw_x);
   EXPECT_EQ(4 + raw_offset_y, GetMostRecentGestureEvent().raw_y);
 
-  event = ObtainMotionEvent(
-      event_time, MotionEvent::ACTION_POINTER_UP, 2, 2, 3, 3, 4, 4);
+  event = ObtainMotionEvent(event_time, MotionEvent::Action::POINTER_UP, 2, 2,
+                            3, 3, 4, 4);
   event.SetRawOffset(raw_offset_x, raw_offset_y);
   EXPECT_TRUE(gesture_provider_->OnTouchEvent(event));
   EXPECT_EQ(ET_GESTURE_END, GetMostRecentGestureEventType());
@@ -2264,8 +2145,8 @@
   EXPECT_EQ(2 + raw_offset_x, GetMostRecentGestureEvent().raw_x);
   EXPECT_EQ(2 + raw_offset_y, GetMostRecentGestureEvent().raw_y);
 
-  event =
-      ObtainMotionEvent(event_time, MotionEvent::ACTION_POINTER_UP, 3, 3, 4, 4);
+  event = ObtainMotionEvent(event_time, MotionEvent::Action::POINTER_UP, 3, 3,
+                            4, 4);
   event.SetRawOffset(raw_offset_x, raw_offset_y);
   EXPECT_TRUE(gesture_provider_->OnTouchEvent(event));
   EXPECT_EQ(ET_GESTURE_END, GetMostRecentGestureEventType());
@@ -2276,8 +2157,7 @@
   EXPECT_EQ(3 + raw_offset_x, GetMostRecentGestureEvent().raw_x);
   EXPECT_EQ(3 + raw_offset_y, GetMostRecentGestureEvent().raw_y);
 
-
-  event = ObtainMotionEvent(event_time, MotionEvent::ACTION_UP, 4, 4);
+  event = ObtainMotionEvent(event_time, MotionEvent::Action::UP, 4, 4);
   event.SetRawOffset(raw_offset_x, raw_offset_y);
   EXPECT_TRUE(gesture_provider_->OnTouchEvent(event));
   EXPECT_EQ(ET_GESTURE_END, GetMostRecentGestureEventType());
@@ -2290,14 +2170,14 @@
 }
 
 // Verify that gesture begin and gesture end events are dispatched correctly
-// when an ACTION_CANCEL is received.
+// when an Action::CANCEL is received.
 TEST_F(GestureProviderTest, GestureBeginAndEndOnCancel) {
   EnableBeginEndTypes();
   base::TimeTicks event_time = base::TimeTicks::Now();
 
   EXPECT_EQ(0U, GetReceivedGestureCount());
   MockMotionEvent event =
-      ObtainMotionEvent(event_time, MotionEvent::ACTION_DOWN, 1, 1);
+      ObtainMotionEvent(event_time, MotionEvent::Action::DOWN, 1, 1);
   EXPECT_TRUE(gesture_provider_->OnTouchEvent(event));
   EXPECT_EQ(ET_GESTURE_BEGIN, GetReceivedGesture(0).type());
   EXPECT_EQ(ET_GESTURE_TAP_DOWN, GetMostRecentGestureEventType());
@@ -2309,8 +2189,8 @@
   EXPECT_EQ(1, GetMostRecentGestureEvent().x);
   EXPECT_EQ(1, GetMostRecentGestureEvent().y);
 
-  event = ObtainMotionEvent(
-      event_time, MotionEvent::ACTION_POINTER_DOWN, 1, 1, 2, 2);
+  event = ObtainMotionEvent(event_time, MotionEvent::Action::POINTER_DOWN, 1, 1,
+                            2, 2);
   EXPECT_TRUE(gesture_provider_->OnTouchEvent(event));
   EXPECT_EQ(ET_GESTURE_BEGIN, GetMostRecentGestureEventType());
   EXPECT_EQ(3U, GetReceivedGestureCount());
@@ -2318,8 +2198,8 @@
   EXPECT_EQ(2, GetMostRecentGestureEvent().x);
   EXPECT_EQ(2, GetMostRecentGestureEvent().y);
 
-  event = ObtainMotionEvent(
-      event_time, MotionEvent::ACTION_POINTER_DOWN, 1, 1, 2, 2, 3, 3);
+  event = ObtainMotionEvent(event_time, MotionEvent::Action::POINTER_DOWN, 1, 1,
+                            2, 2, 3, 3);
   EXPECT_TRUE(gesture_provider_->OnTouchEvent(event));
   EXPECT_EQ(ET_GESTURE_BEGIN, GetMostRecentGestureEventType());
   EXPECT_EQ(4U, GetReceivedGestureCount());
@@ -2327,8 +2207,8 @@
   EXPECT_EQ(3, GetMostRecentGestureEvent().x);
   EXPECT_EQ(3, GetMostRecentGestureEvent().y);
 
-  event = ObtainMotionEvent(
-      event_time, MotionEvent::ACTION_CANCEL, 1, 1, 2, 2, 3, 3);
+  event = ObtainMotionEvent(event_time, MotionEvent::Action::CANCEL, 1, 1, 2, 2,
+                            3, 3);
   EXPECT_TRUE(gesture_provider_->OnTouchEvent(event));
   EXPECT_EQ(5U, GetReceivedGestureCount());
   EXPECT_EQ(3, GetReceivedGesture(4).details.touch_points());
@@ -2336,8 +2216,8 @@
   EXPECT_EQ(1, GetMostRecentGestureEvent().x);
   EXPECT_EQ(1, GetMostRecentGestureEvent().y);
 
-  event = ObtainMotionEvent(
-      event_time, MotionEvent::ACTION_CANCEL, 1, 1, 3, 3);
+  event =
+      ObtainMotionEvent(event_time, MotionEvent::Action::CANCEL, 1, 1, 3, 3);
   EXPECT_TRUE(gesture_provider_->OnTouchEvent(event));
   EXPECT_EQ(6U, GetReceivedGestureCount());
   EXPECT_EQ(2, GetMostRecentGestureEvent().details.touch_points());
@@ -2345,8 +2225,7 @@
   EXPECT_EQ(1, GetMostRecentGestureEvent().x);
   EXPECT_EQ(1, GetMostRecentGestureEvent().y);
 
-  event = ObtainMotionEvent(
-      event_time, MotionEvent::ACTION_CANCEL, 3, 3);
+  event = ObtainMotionEvent(event_time, MotionEvent::Action::CANCEL, 3, 3);
   EXPECT_TRUE(gesture_provider_->OnTouchEvent(event));
   EXPECT_EQ(1, GetMostRecentGestureEvent().details.touch_points());
   EXPECT_EQ(ET_GESTURE_END, GetMostRecentGestureEvent().type());
@@ -2356,7 +2235,7 @@
 
 // Test a simple two finger tap
 TEST_F(GestureProviderTest, TwoFingerTap) {
-  // The time between ACTION_POINTER_DOWN and ACTION_POINTER_UP must be <= the
+  // The time between Action::POINTER_DOWN and Action::POINTER_UP must be <= the
   // two finger tap delay.
   EnableTwoFingerTap(kMaxTwoFingerTapSeparation, base::TimeDelta());
   const float scaled_touch_slop = GetTouchSlop();
@@ -2364,37 +2243,23 @@
   base::TimeTicks event_time = base::TimeTicks::Now();
 
   MockMotionEvent event =
-      ObtainMotionEvent(event_time, MotionEvent::ACTION_DOWN, 0, 0);
+      ObtainMotionEvent(event_time, MotionEvent::Action::DOWN, 0, 0);
   EXPECT_TRUE(gesture_provider_->OnTouchEvent(event));
 
-  event = ObtainMotionEvent(event_time,
-                            MotionEvent::ACTION_MOVE,
-                            0,
+  event = ObtainMotionEvent(event_time, MotionEvent::Action::MOVE, 0,
                             scaled_touch_slop / 2);
 
-  event = ObtainMotionEvent(event_time,
-                            MotionEvent::ACTION_POINTER_DOWN,
-                            0,
-                            0,
-                            kMaxTwoFingerTapSeparation / 2,
-                            0);
+  event = ObtainMotionEvent(event_time, MotionEvent::Action::POINTER_DOWN, 0, 0,
+                            kMaxTwoFingerTapSeparation / 2, 0);
   EXPECT_TRUE(gesture_provider_->OnTouchEvent(event));
 
-  event =
-      ObtainMotionEvent(event_time,
-                        MotionEvent::ACTION_MOVE,
-                        0,
-                        -scaled_touch_slop / 2,
-                        kMaxTwoFingerTapSeparation / 2 + scaled_touch_slop / 2,
-                        0);
+  event = ObtainMotionEvent(
+      event_time, MotionEvent::Action::MOVE, 0, -scaled_touch_slop / 2,
+      kMaxTwoFingerTapSeparation / 2 + scaled_touch_slop / 2, 0);
   EXPECT_TRUE(gesture_provider_->OnTouchEvent(event));
 
-  event = ObtainMotionEvent(event_time,
-                            MotionEvent::ACTION_POINTER_UP,
-                            0,
-                            0,
-                            kMaxTwoFingerTapSeparation,
-                            0);
+  event = ObtainMotionEvent(event_time, MotionEvent::Action::POINTER_UP, 0, 0,
+                            kMaxTwoFingerTapSeparation, 0);
   EXPECT_TRUE(gesture_provider_->OnTouchEvent(event));
 
   EXPECT_EQ(ET_GESTURE_TAP_DOWN, GetReceivedGesture(0).type());
@@ -2413,28 +2278,20 @@
   base::TimeTicks event_time = base::TimeTicks::Now();
 
   MockMotionEvent event = ObtainMotionEvent(
-      event_time, MotionEvent::ACTION_DOWN, kFakeCoordX, kFakeCoordY);
+      event_time, MotionEvent::Action::DOWN, kFakeCoordX, kFakeCoordY);
   EXPECT_TRUE(gesture_provider_->OnTouchEvent(event));
 
-  event = ObtainMotionEvent(event_time,
-                            MotionEvent::ACTION_POINTER_DOWN,
-                            kFakeCoordX,
-                            kFakeCoordY,
-                            kFakeCoordX,
-                            kFakeCoordY);
+  event = ObtainMotionEvent(event_time, MotionEvent::Action::POINTER_DOWN,
+                            kFakeCoordX, kFakeCoordY, kFakeCoordX, kFakeCoordY);
   EXPECT_TRUE(gesture_provider_->OnTouchEvent(event));
 
   event = ObtainMotionEvent(
-      event_time, MotionEvent::ACTION_MOVE, kFakeCoordX, kFakeCoordY,
+      event_time, MotionEvent::Action::MOVE, kFakeCoordX, kFakeCoordY,
       kFakeCoordX + 2 * scaled_touch_slop + 2, kFakeCoordY);
   EXPECT_TRUE(gesture_provider_->OnTouchEvent(event));
 
-  event = ObtainMotionEvent(event_time,
-                            MotionEvent::ACTION_POINTER_UP,
-                            kFakeCoordX,
-                            kFakeCoordY,
-                            kFakeCoordX,
-                            kFakeCoordY);
+  event = ObtainMotionEvent(event_time, MotionEvent::Action::POINTER_UP,
+                            kFakeCoordX, kFakeCoordY, kFakeCoordX, kFakeCoordY);
   EXPECT_TRUE(gesture_provider_->OnTouchEvent(event));
 
   EXPECT_EQ(ET_GESTURE_TAP_DOWN, GetReceivedGesture(0).type());
@@ -2458,28 +2315,21 @@
   base::TimeTicks event_time = base::TimeTicks::Now();
 
   MockMotionEvent event = ObtainMotionEvent(
-      event_time, MotionEvent::ACTION_DOWN, kFakeCoordX, kFakeCoordY);
+      event_time, MotionEvent::Action::DOWN, kFakeCoordX, kFakeCoordY);
   EXPECT_TRUE(gesture_provider_->OnTouchEvent(event));
 
-  event = ObtainMotionEvent(event_time,
-                            MotionEvent::ACTION_MOVE,
-                            kFakeCoordX,
+  event = ObtainMotionEvent(event_time, MotionEvent::Action::MOVE, kFakeCoordX,
                             kFakeCoordY);
 
-  event = ObtainMotionEvent(event_time,
-                            MotionEvent::ACTION_POINTER_DOWN,
-                            kFakeCoordX,
-                            kFakeCoordY,
-                            kFakeCoordX + kMaxTwoFingerTapSeparation / 2,
-                            kFakeCoordY);
+  event = ObtainMotionEvent(
+      event_time, MotionEvent::Action::POINTER_DOWN, kFakeCoordX, kFakeCoordY,
+      kFakeCoordX + kMaxTwoFingerTapSeparation / 2, kFakeCoordY);
   EXPECT_TRUE(gesture_provider_->OnTouchEvent(event));
 
-  event = ObtainMotionEvent(event_time + kOneSecond + kOneMicrosecond,
-                            MotionEvent::ACTION_POINTER_UP,
-                            kFakeCoordX,
-                            kFakeCoordY,
-                            kFakeCoordX + kMaxTwoFingerTapSeparation / 2,
-                            kFakeCoordY);
+  event = ObtainMotionEvent(
+      event_time + kOneSecond + kOneMicrosecond,
+      MotionEvent::Action::POINTER_UP, kFakeCoordX, kFakeCoordY,
+      kFakeCoordX + kMaxTwoFingerTapSeparation / 2, kFakeCoordY);
   EXPECT_TRUE(gesture_provider_->OnTouchEvent(event));
 
   EXPECT_EQ(ET_GESTURE_TAP_DOWN, GetReceivedGesture(0).type());
@@ -2493,23 +2343,17 @@
   base::TimeTicks event_time = base::TimeTicks::Now();
 
   MockMotionEvent event = ObtainMotionEvent(
-      event_time, MotionEvent::ACTION_DOWN, kFakeCoordX, kFakeCoordY);
+      event_time, MotionEvent::Action::DOWN, kFakeCoordX, kFakeCoordY);
   EXPECT_TRUE(gesture_provider_->OnTouchEvent(event));
 
-  event = ObtainMotionEvent(event_time,
-                            MotionEvent::ACTION_POINTER_DOWN,
-                            kFakeCoordX,
-                            kFakeCoordY,
-                            kFakeCoordX + kMaxTwoFingerTapSeparation,
-                            kFakeCoordY);
+  event = ObtainMotionEvent(
+      event_time, MotionEvent::Action::POINTER_DOWN, kFakeCoordX, kFakeCoordY,
+      kFakeCoordX + kMaxTwoFingerTapSeparation, kFakeCoordY);
   EXPECT_TRUE(gesture_provider_->OnTouchEvent(event));
 
-  event = ObtainMotionEvent(event_time,
-                            MotionEvent::ACTION_POINTER_UP,
-                            kFakeCoordX,
-                            kFakeCoordY,
-                            kFakeCoordX + kMaxTwoFingerTapSeparation,
-                            kFakeCoordY);
+  event = ObtainMotionEvent(
+      event_time, MotionEvent::Action::POINTER_UP, kFakeCoordX, kFakeCoordY,
+      kFakeCoordX + kMaxTwoFingerTapSeparation, kFakeCoordY);
   EXPECT_TRUE(gesture_provider_->OnTouchEvent(event));
 
   EXPECT_EQ(ET_GESTURE_TAP_DOWN, GetReceivedGesture(0).type());
@@ -2535,7 +2379,7 @@
 
   // First Finger Down
   MockMotionEvent event =
-      ObtainMotionEvent(event_time, MotionEvent::ACTION_DOWN);
+      ObtainMotionEvent(event_time, MotionEvent::Action::DOWN);
   event.SetPrimaryPointerId(motion_event_id);
   event.SetRawOffset(raw_offset_x, raw_offset_y);
   EXPECT_TRUE(gesture_provider_->OnTouchEvent(event));
@@ -2544,7 +2388,7 @@
   EXPECT_EQ(kFakeCoordY, GetMostRecentGestureEvent().y);
 
   // Second Finger Down
-  event = ObtainMotionEvent(event_time, MotionEvent::ACTION_POINTER_DOWN,
+  event = ObtainMotionEvent(event_time, MotionEvent::Action::POINTER_DOWN,
                             kFakeCoordX, kFakeCoordY, secondary_coord_x,
                             secondary_coord_y);
 
@@ -2557,7 +2401,7 @@
   // Move second finger by exactly the touch slop. This shouldn't yet generate
   // a Pinch Begin.
   secondary_coord_y += touch_slop * 2;
-  event = ObtainMotionEvent(event_time, MotionEvent::ACTION_MOVE, kFakeCoordX,
+  event = ObtainMotionEvent(event_time, MotionEvent::Action::MOVE, kFakeCoordX,
                             kFakeCoordY, secondary_coord_x, secondary_coord_y);
   event.SetPrimaryPointerId(motion_event_id);
   event.SetRawOffset(raw_offset_x, raw_offset_y);
@@ -2569,7 +2413,7 @@
 
   // Move second finger that should *just* cross the slop threshold.
   secondary_coord_y += 1;
-  event = ObtainMotionEvent(event_time, MotionEvent::ACTION_MOVE, kFakeCoordX,
+  event = ObtainMotionEvent(event_time, MotionEvent::Action::MOVE, kFakeCoordX,
                             kFakeCoordY, secondary_coord_x, secondary_coord_y);
   event.SetPrimaryPointerId(motion_event_id);
   event.SetRawOffset(raw_offset_x, raw_offset_y);
@@ -2600,7 +2444,7 @@
 
   // First Finger Down
   MockMotionEvent event =
-      ObtainMotionEvent(event_time, MotionEvent::ACTION_DOWN);
+      ObtainMotionEvent(event_time, MotionEvent::Action::DOWN);
   event.SetPrimaryPointerId(motion_event_id);
   event.SetRawOffset(raw_offset_x, raw_offset_y);
   EXPECT_TRUE(gesture_provider_->OnTouchEvent(event));
@@ -2609,7 +2453,7 @@
   EXPECT_EQ(kFakeCoordY, GetMostRecentGestureEvent().y);
 
   // Second Finger Down
-  event = ObtainMotionEvent(event_time, MotionEvent::ACTION_POINTER_DOWN,
+  event = ObtainMotionEvent(event_time, MotionEvent::Action::POINTER_DOWN,
                             kFakeCoordX, kFakeCoordY, secondary_coord_x,
                             secondary_coord_y);
 
@@ -2621,7 +2465,7 @@
 
   // Move second finger enough to exceed the touch slop and start zooming.
   secondary_coord_y -= (touch_slop * 2 + 1);
-  event = ObtainMotionEvent(event_time, MotionEvent::ACTION_MOVE, kFakeCoordX,
+  event = ObtainMotionEvent(event_time, MotionEvent::Action::MOVE, kFakeCoordX,
                             kFakeCoordY, secondary_coord_x, secondary_coord_y);
   event.SetPrimaryPointerId(motion_event_id);
   event.SetRawOffset(raw_offset_x, raw_offset_y);
@@ -2635,7 +2479,7 @@
   // Move second finger so that the span becomes smaller than the min scaling
   // span. The pinch should end but we should receive an update before it does.
   secondary_coord_y -= touch_slop * 2;
-  event = ObtainMotionEvent(event_time, MotionEvent::ACTION_MOVE, kFakeCoordX,
+  event = ObtainMotionEvent(event_time, MotionEvent::Action::MOVE, kFakeCoordX,
                             kFakeCoordY, secondary_coord_x, secondary_coord_y);
   event.SetPrimaryPointerId(motion_event_id);
   event.SetRawOffset(raw_offset_x, raw_offset_y);
@@ -2670,7 +2514,7 @@
 
   // First Finger Down
   MockMotionEvent event =
-      ObtainMotionEvent(event_time, MotionEvent::ACTION_DOWN);
+      ObtainMotionEvent(event_time, MotionEvent::Action::DOWN);
   event.SetPrimaryPointerId(motion_event_id);
   event.SetRawOffset(raw_offset_x, raw_offset_y);
   EXPECT_TRUE(gesture_provider_->OnTouchEvent(event));
@@ -2679,7 +2523,7 @@
   EXPECT_EQ(kFakeCoordY, GetMostRecentGestureEvent().y);
 
   // Second Finger Down
-  event = ObtainMotionEvent(event_time, MotionEvent::ACTION_POINTER_DOWN,
+  event = ObtainMotionEvent(event_time, MotionEvent::Action::POINTER_DOWN,
                             kFakeCoordX, kFakeCoordY, secondary_coord_x,
                             secondary_coord_y);
 
@@ -2692,7 +2536,7 @@
   // Move second finger to exceed the touch slop. This shouldn't yet generate
   // a Pinch Begin since we're still within the minimum scaling span.
   secondary_coord_y += touch_slop * 2 + 1;
-  event = ObtainMotionEvent(event_time, MotionEvent::ACTION_MOVE, kFakeCoordX,
+  event = ObtainMotionEvent(event_time, MotionEvent::Action::MOVE, kFakeCoordX,
                             kFakeCoordY, secondary_coord_x, secondary_coord_y);
   event.SetPrimaryPointerId(motion_event_id);
   event.SetRawOffset(raw_offset_x, raw_offset_y);
@@ -2704,7 +2548,7 @@
 
   // Move second finger that should *just* cross the min scaling span threshold.
   secondary_coord_y = kFakeCoordY + min_scaling_span + 1;
-  event = ObtainMotionEvent(event_time, MotionEvent::ACTION_MOVE, kFakeCoordX,
+  event = ObtainMotionEvent(event_time, MotionEvent::Action::MOVE, kFakeCoordX,
                             kFakeCoordY, secondary_coord_x, secondary_coord_y);
   event.SetPrimaryPointerId(motion_event_id);
   event.SetRawOffset(raw_offset_x, raw_offset_y);
@@ -2738,17 +2582,14 @@
 
   // First finger down.
   MockMotionEvent event =
-      ObtainMotionEvent(event_time, MotionEvent::ACTION_DOWN);
+      ObtainMotionEvent(event_time, MotionEvent::Action::DOWN);
   EXPECT_TRUE(gesture_provider_->OnTouchEvent(event));
   EXPECT_EQ(ET_GESTURE_TAP_DOWN, GetMostRecentGestureEventType());
   EXPECT_EQ(1, GetMostRecentGestureEvent().details.touch_points());
 
   // Second finger down.
-  event = ObtainMotionEvent(event_time,
-                            MotionEvent::ACTION_POINTER_DOWN,
-                            kFakeCoordX,
-                            kFakeCoordY,
-                            secondary_coord_x,
+  event = ObtainMotionEvent(event_time, MotionEvent::Action::POINTER_DOWN,
+                            kFakeCoordX, kFakeCoordY, secondary_coord_x,
                             secondary_coord_y);
 
   gesture_provider_->OnTouchEvent(event);
@@ -2758,12 +2599,8 @@
   // Move second finger.
   secondary_coord_x += 5 * touch_slop;
   secondary_coord_y += 5 * touch_slop;
-  event = ObtainMotionEvent(event_time,
-                            MotionEvent::ACTION_MOVE,
-                            kFakeCoordX,
-                            kFakeCoordY,
-                            secondary_coord_x,
-                            secondary_coord_y);
+  event = ObtainMotionEvent(event_time, MotionEvent::Action::MOVE, kFakeCoordX,
+                            kFakeCoordY, secondary_coord_x, secondary_coord_y);
 
   EXPECT_TRUE(gesture_provider_->OnTouchEvent(event));
   EXPECT_EQ(2, GetMostRecentGestureEvent().details.touch_points());
@@ -2774,12 +2611,9 @@
 
   // Small move, shouldn't trigger pinch.
   gestures_.clear();
-  event = ObtainMotionEvent(event_time,
-                            MotionEvent::ACTION_MOVE,
-                            kFakeCoordX,
-                            kFakeCoordY,
-                            secondary_coord_x + kMinPinchUpdateDistance,
-                            secondary_coord_y);
+  event = ObtainMotionEvent(
+      event_time, MotionEvent::Action::MOVE, kFakeCoordX, kFakeCoordY,
+      secondary_coord_x + kMinPinchUpdateDistance, secondary_coord_y);
 
   EXPECT_TRUE(gesture_provider_->OnTouchEvent(event));
   EXPECT_FALSE(HasReceivedGesture(ET_GESTURE_PINCH_UPDATE));
@@ -2789,9 +2623,7 @@
   // need to overshoot kMinPinchUpdateDistance by a fair bit, as the span
   // calculation factors in touch radius.
   const float kOvershootMinPinchUpdateDistance = 3;
-  event = ObtainMotionEvent(event_time,
-                            MotionEvent::ACTION_MOVE,
-                            kFakeCoordX,
+  event = ObtainMotionEvent(event_time, MotionEvent::Action::MOVE, kFakeCoordX,
                             kFakeCoordY,
                             secondary_coord_x + kMinPinchUpdateDistance +
                                 kOvershootMinPinchUpdateDistance,
@@ -2810,7 +2642,7 @@
 
   base::TimeTicks event_time = base::TimeTicks::Now();
   MockMotionEvent event =
-      ObtainMotionEvent(event_time, MotionEvent::ACTION_DOWN);
+      ObtainMotionEvent(event_time, MotionEvent::Action::DOWN);
   EXPECT_TRUE(gesture_provider_->OnTouchEvent(event));
 
   EXPECT_EQ(ET_GESTURE_TAP_DOWN, GetMostRecentGestureEventType());
@@ -2820,7 +2652,7 @@
             GetMostRecentGestureEvent().details.bounding_box_f().height());
 
   event =
-      ObtainMotionEvent(event_time + kOneMicrosecond, MotionEvent::ACTION_UP);
+      ObtainMotionEvent(event_time + kOneMicrosecond, MotionEvent::Action::UP);
   EXPECT_TRUE(gesture_provider_->OnTouchEvent(event));
   EXPECT_EQ(ET_GESTURE_TAP, GetMostRecentGestureEventType());
   EXPECT_EQ(kMinGestureBoundsLength,
@@ -2836,7 +2668,7 @@
 
   base::TimeTicks event_time = base::TimeTicks::Now();
   MockMotionEvent event =
-      ObtainMotionEvent(event_time, MotionEvent::ACTION_DOWN);
+      ObtainMotionEvent(event_time, MotionEvent::Action::DOWN);
   EXPECT_TRUE(gesture_provider_->OnTouchEvent(event));
 
   EXPECT_EQ(ET_GESTURE_TAP_DOWN, GetMostRecentGestureEventType());
@@ -2846,7 +2678,7 @@
             GetMostRecentGestureEvent().details.bounding_box_f().height());
 
   event =
-      ObtainMotionEvent(event_time + kOneMicrosecond, MotionEvent::ACTION_UP);
+      ObtainMotionEvent(event_time + kOneMicrosecond, MotionEvent::Action::UP);
   EXPECT_TRUE(gesture_provider_->OnTouchEvent(event));
   EXPECT_EQ(ET_GESTURE_TAP, GetMostRecentGestureEventType());
   EXPECT_EQ(kMaxGestureBoundsLength,
@@ -2858,19 +2690,19 @@
 TEST_F(GestureProviderTest, ZeroRadiusBoundingBox) {
   base::TimeTicks event_time = base::TimeTicks::Now();
   MockMotionEvent event =
-      ObtainMotionEvent(event_time, MotionEvent::ACTION_DOWN, 10, 20);
+      ObtainMotionEvent(event_time, MotionEvent::Action::DOWN, 10, 20);
   event.SetTouchMajor(0);
   EXPECT_TRUE(gesture_provider_->OnTouchEvent(event));
   EXPECT_EQ(gfx::RectF(10, 20, 0, 0),
             GetMostRecentGestureEvent().details.bounding_box_f());
 
-  event = ObtainMotionEvent(
-      event_time, MotionEvent::ACTION_POINTER_DOWN, 10, 20, 110, 120);
+  event = ObtainMotionEvent(event_time, MotionEvent::Action::POINTER_DOWN, 10,
+                            20, 110, 120);
   event.SetTouchMajor(0);
   EXPECT_TRUE(gesture_provider_->OnTouchEvent(event));
 
-  event = ObtainMotionEvent(
-      event_time, MotionEvent::ACTION_MOVE, 10, 20, 110, 150);
+  event = ObtainMotionEvent(event_time, MotionEvent::Action::MOVE, 10, 20, 110,
+                            150);
   event.SetTouchMajor(0);
   EXPECT_TRUE(gesture_provider_->OnTouchEvent(event));
 
@@ -2889,33 +2721,33 @@
 
   base::TimeTicks event_time = base::TimeTicks::Now();
   MockMotionEvent event =
-      ObtainMotionEvent(event_time, MotionEvent::ACTION_DOWN);
+      ObtainMotionEvent(event_time, MotionEvent::Action::DOWN);
   event.SetTouchMajor(0);
-  event.SetToolType(0, MotionEvent::TOOL_TYPE_MOUSE);
+  event.SetToolType(0, MotionEvent::ToolType::MOUSE);
   EXPECT_TRUE(gesture_provider_->OnTouchEvent(event));
 
   EXPECT_EQ(ET_GESTURE_TAP_DOWN, GetMostRecentGestureEventType());
-  EXPECT_EQ(MotionEvent::TOOL_TYPE_MOUSE,
+  EXPECT_EQ(MotionEvent::ToolType::MOUSE,
             GetMostRecentGestureEvent().primary_tool_type);
   EXPECT_EQ(0.f, GetMostRecentGestureEvent().details.bounding_box_f().width());
   EXPECT_EQ(0.f, GetMostRecentGestureEvent().details.bounding_box_f().height());
 
   event =
-      ObtainMotionEvent(event_time + kOneMicrosecond, MotionEvent::ACTION_UP);
+      ObtainMotionEvent(event_time + kOneMicrosecond, MotionEvent::Action::UP);
   event.SetTouchMajor(1);
-  event.SetToolType(0, MotionEvent::TOOL_TYPE_STYLUS);
+  event.SetToolType(0, MotionEvent::ToolType::STYLUS);
   EXPECT_TRUE(gesture_provider_->OnTouchEvent(event));
   EXPECT_EQ(ET_GESTURE_TAP, GetMostRecentGestureEventType());
-  EXPECT_EQ(MotionEvent::TOOL_TYPE_STYLUS,
+  EXPECT_EQ(MotionEvent::ToolType::STYLUS,
             GetMostRecentGestureEvent().primary_tool_type);
   EXPECT_EQ(0, GetMostRecentGestureEvent().details.bounding_box_f().width());
   EXPECT_EQ(0, GetMostRecentGestureEvent().details.bounding_box_f().height());
 
-  event = ObtainMotionEvent(event_time, MotionEvent::ACTION_DOWN);
+  event = ObtainMotionEvent(event_time, MotionEvent::Action::DOWN);
   event.SetTouchMajor(2.f * kMaxGestureBoundsLength);
-  event.SetToolType(0, MotionEvent::TOOL_TYPE_MOUSE);
+  event.SetToolType(0, MotionEvent::ToolType::MOUSE);
   EXPECT_TRUE(gesture_provider_->OnTouchEvent(event));
-  EXPECT_EQ(MotionEvent::TOOL_TYPE_MOUSE,
+  EXPECT_EQ(MotionEvent::ToolType::MOUSE,
             GetMostRecentGestureEvent().primary_tool_type);
   EXPECT_EQ(ET_GESTURE_TAP_DOWN, GetMostRecentGestureEventType());
   EXPECT_EQ(2.f * kMaxGestureBoundsLength,
@@ -2924,12 +2756,12 @@
             GetMostRecentGestureEvent().details.bounding_box_f().height());
 
   event =
-      ObtainMotionEvent(event_time + kOneMicrosecond, MotionEvent::ACTION_UP);
+      ObtainMotionEvent(event_time + kOneMicrosecond, MotionEvent::Action::UP);
   event.SetTouchMajor(2.f * kMaxGestureBoundsLength);
-  event.SetToolType(0, MotionEvent::TOOL_TYPE_ERASER);
+  event.SetToolType(0, MotionEvent::ToolType::ERASER);
   EXPECT_TRUE(gesture_provider_->OnTouchEvent(event));
   EXPECT_EQ(ET_GESTURE_TAP, GetMostRecentGestureEventType());
-  EXPECT_EQ(MotionEvent::TOOL_TYPE_ERASER,
+  EXPECT_EQ(MotionEvent::ToolType::ERASER,
             GetMostRecentGestureEvent().primary_tool_type);
   EXPECT_EQ(2.f * kMaxGestureBoundsLength,
             GetMostRecentGestureEvent().details.bounding_box_f().width());
@@ -2946,7 +2778,7 @@
   SetShowPressAndLongPressTimeout(showpress_timeout, longpress_timeout);
 
   MockMotionEvent event =
-      ObtainMotionEvent(event_time, MotionEvent::ACTION_DOWN, 10, 10);
+      ObtainMotionEvent(event_time, MotionEvent::Action::DOWN, 10, 10);
   event.SetTouchMajor(10);
 
   EXPECT_TRUE(gesture_provider_->OnTouchEvent(event));
@@ -2955,12 +2787,12 @@
   EXPECT_EQ(gfx::RectF(5, 5, 10, 10),
             GetMostRecentGestureEvent().details.bounding_box_f());
 
-  event = ObtainMotionEvent(
-      event_time + kOneMicrosecond, MotionEvent::ACTION_MOVE, 11, 9);
+  event = ObtainMotionEvent(event_time + kOneMicrosecond,
+                            MotionEvent::Action::MOVE, 11, 9);
   event.SetTouchMajor(20);
   EXPECT_TRUE(gesture_provider_->OnTouchEvent(event));
-  event = ObtainMotionEvent(
-      event_time + kOneMicrosecond, MotionEvent::ACTION_MOVE, 8, 11);
+  event = ObtainMotionEvent(event_time + kOneMicrosecond,
+                            MotionEvent::Action::MOVE, 8, 11);
   event.SetTouchMajor(10);
   EXPECT_TRUE(gesture_provider_->OnTouchEvent(event));
   RunTasksAndWait(showpress_timeout + kOneMicrosecond);
@@ -2969,7 +2801,7 @@
             GetMostRecentGestureEvent().details.bounding_box_f());
 
   event =
-      ObtainMotionEvent(event_time + kOneMicrosecond, MotionEvent::ACTION_UP);
+      ObtainMotionEvent(event_time + kOneMicrosecond, MotionEvent::Action::UP);
   event.SetTouchMajor(30);
   EXPECT_TRUE(gesture_provider_->OnTouchEvent(event));
   EXPECT_EQ(ET_GESTURE_TAP, GetMostRecentGestureEventType());
@@ -2988,9 +2820,9 @@
   base::TimeTicks event_time = base::TimeTicks::Now();
 
   MockMotionEvent event =
-      ObtainMotionEvent(event_time, MotionEvent::ACTION_DOWN);
+      ObtainMotionEvent(event_time, MotionEvent::Action::DOWN);
   EXPECT_TRUE(gesture_provider_->OnTouchEvent(event));
-  event = ObtainMotionEvent(event_time, MotionEvent::ACTION_UP);
+  event = ObtainMotionEvent(event_time, MotionEvent::Action::UP);
   gesture_provider_->OnTouchEvent(event);
   EXPECT_EQ(ET_GESTURE_TAP, GetMostRecentGestureEventType());
   EXPECT_EQ(1, GetMostRecentGestureEvent().details.tap_count());
@@ -2998,9 +2830,9 @@
   // A second tap after the double-tap timeout window will not increment
   // the tap count.
   event_time += GetDoubleTapTimeout() + kOneMicrosecond;
-  event = ObtainMotionEvent(event_time, MotionEvent::ACTION_DOWN);
+  event = ObtainMotionEvent(event_time, MotionEvent::Action::DOWN);
   EXPECT_TRUE(gesture_provider_->OnTouchEvent(event));
-  event = ObtainMotionEvent(event_time, MotionEvent::ACTION_UP);
+  event = ObtainMotionEvent(event_time, MotionEvent::Action::UP);
   gesture_provider_->OnTouchEvent(event);
   EXPECT_EQ(ET_GESTURE_TAP, GetMostRecentGestureEventType());
   EXPECT_EQ(1, GetMostRecentGestureEvent().details.tap_count());
@@ -3008,9 +2840,9 @@
   // A secondary tap within the tap repeat period should increment
   // the tap count.
   event_time += GetValidDoubleTapDelay();
-  event = ObtainMotionEvent(event_time, MotionEvent::ACTION_DOWN);
+  event = ObtainMotionEvent(event_time, MotionEvent::Action::DOWN);
   EXPECT_TRUE(gesture_provider_->OnTouchEvent(event));
-  event = ObtainMotionEvent(event_time, MotionEvent::ACTION_UP);
+  event = ObtainMotionEvent(event_time, MotionEvent::Action::UP);
   EXPECT_TRUE(gesture_provider_->OnTouchEvent(event));
   EXPECT_EQ(ET_GESTURE_TAP, GetMostRecentGestureEventType());
   EXPECT_EQ(2, GetMostRecentGestureEvent().details.tap_count());
@@ -3018,19 +2850,19 @@
   // A secondary tap within the tap repeat location threshold should increment
   // the tap count.
   event_time += GetValidDoubleTapDelay();
-  event = ObtainMotionEvent(event_time, MotionEvent::ACTION_DOWN, kFakeCoordX,
+  event = ObtainMotionEvent(event_time, MotionEvent::Action::DOWN, kFakeCoordX,
                             kFakeCoordY + GetTouchSlop() / 2);
   EXPECT_TRUE(gesture_provider_->OnTouchEvent(event));
-  event = ObtainMotionEvent(event_time, MotionEvent::ACTION_UP);
+  event = ObtainMotionEvent(event_time, MotionEvent::Action::UP);
   EXPECT_TRUE(gesture_provider_->OnTouchEvent(event));
   EXPECT_EQ(ET_GESTURE_TAP, GetMostRecentGestureEventType());
   EXPECT_EQ(3, GetMostRecentGestureEvent().details.tap_count());
 
   // The tap count should reset after hitting the repeat length.
   event_time += GetValidDoubleTapDelay();
-  event = ObtainMotionEvent(event_time, MotionEvent::ACTION_DOWN);
+  event = ObtainMotionEvent(event_time, MotionEvent::Action::DOWN);
   EXPECT_TRUE(gesture_provider_->OnTouchEvent(event));
-  event = ObtainMotionEvent(event_time, MotionEvent::ACTION_UP);
+  event = ObtainMotionEvent(event_time, MotionEvent::Action::UP);
   EXPECT_TRUE(gesture_provider_->OnTouchEvent(event));
   EXPECT_EQ(ET_GESTURE_TAP, GetMostRecentGestureEventType());
   EXPECT_EQ(1, GetMostRecentGestureEvent().details.tap_count());
@@ -3038,18 +2870,18 @@
   // If double-tap is enabled, the tap repeat count should always be 1.
   gesture_provider_->SetDoubleTapSupportForPlatformEnabled(true);
   event_time += GetValidDoubleTapDelay();
-  event = ObtainMotionEvent(event_time, MotionEvent::ACTION_DOWN);
+  event = ObtainMotionEvent(event_time, MotionEvent::Action::DOWN);
   EXPECT_TRUE(gesture_provider_->OnTouchEvent(event));
-  event = ObtainMotionEvent(event_time, MotionEvent::ACTION_UP);
+  event = ObtainMotionEvent(event_time, MotionEvent::Action::UP);
   EXPECT_TRUE(gesture_provider_->OnTouchEvent(event));
   RunTasksAndWait(GetDoubleTapTimeout());
   EXPECT_EQ(ET_GESTURE_TAP, GetMostRecentGestureEventType());
   EXPECT_EQ(1, GetMostRecentGestureEvent().details.tap_count());
 
   event_time += GetValidDoubleTapDelay();
-  event = ObtainMotionEvent(event_time, MotionEvent::ACTION_DOWN);
+  event = ObtainMotionEvent(event_time, MotionEvent::Action::DOWN);
   EXPECT_TRUE(gesture_provider_->OnTouchEvent(event));
-  event = ObtainMotionEvent(event_time, MotionEvent::ACTION_UP);
+  event = ObtainMotionEvent(event_time, MotionEvent::Action::UP);
   EXPECT_TRUE(gesture_provider_->OnTouchEvent(event));
   RunTasksAndWait(GetDoubleTapTimeout());
   EXPECT_EQ(ET_GESTURE_TAP, GetMostRecentGestureEventType());
@@ -3064,9 +2896,9 @@
   base::TimeTicks event_time = base::TimeTicks::Now();
 
   MockMotionEvent event =
-      ObtainMotionEvent(event_time, MotionEvent::ACTION_DOWN);
+      ObtainMotionEvent(event_time, MotionEvent::Action::DOWN);
   EXPECT_TRUE(gesture_provider_->OnTouchEvent(event));
-  event = ObtainMotionEvent(event_time, MotionEvent::ACTION_UP);
+  event = ObtainMotionEvent(event_time, MotionEvent::Action::UP);
   gesture_provider_->OnTouchEvent(event);
   EXPECT_EQ(ET_GESTURE_TAP, GetMostRecentGestureEventType());
   EXPECT_EQ(1, GetMostRecentGestureEvent().details.tap_count());
@@ -3074,18 +2906,18 @@
   // Repeated taps should still produce a tap count of 1 if the
   // tap repeat length is 1.
   event_time += GetValidDoubleTapDelay();
-  event = ObtainMotionEvent(event_time, MotionEvent::ACTION_DOWN);
+  event = ObtainMotionEvent(event_time, MotionEvent::Action::DOWN);
   EXPECT_TRUE(gesture_provider_->OnTouchEvent(event));
-  event = ObtainMotionEvent(event_time, MotionEvent::ACTION_UP);
+  event = ObtainMotionEvent(event_time, MotionEvent::Action::UP);
   EXPECT_TRUE(gesture_provider_->OnTouchEvent(event));
   EXPECT_EQ(ET_GESTURE_TAP, GetMostRecentGestureEventType());
   EXPECT_EQ(1, GetMostRecentGestureEvent().details.tap_count());
 
   event_time += GetValidDoubleTapDelay();
-  event = ObtainMotionEvent(event_time, MotionEvent::ACTION_DOWN, kFakeCoordX,
+  event = ObtainMotionEvent(event_time, MotionEvent::Action::DOWN, kFakeCoordX,
                             kFakeCoordY + GetTouchSlop() / 2);
   EXPECT_TRUE(gesture_provider_->OnTouchEvent(event));
-  event = ObtainMotionEvent(event_time, MotionEvent::ACTION_UP);
+  event = ObtainMotionEvent(event_time, MotionEvent::Action::UP);
   EXPECT_TRUE(gesture_provider_->OnTouchEvent(event));
   EXPECT_EQ(ET_GESTURE_TAP, GetMostRecentGestureEventType());
   EXPECT_EQ(1, GetMostRecentGestureEvent().details.tap_count());
diff --git a/ui/events/gesture_detection/gesture_touch_uma_histogram.cc b/ui/events/gesture_detection/gesture_touch_uma_histogram.cc
index db52a8a..7afcbf4 100644
--- a/ui/events/gesture_detection/gesture_touch_uma_histogram.cc
+++ b/ui/events/gesture_detection/gesture_touch_uma_histogram.cc
@@ -22,19 +22,19 @@
 }
 
 void GestureTouchUMAHistogram::RecordTouchEvent(const MotionEvent& event) {
-  if (event.GetAction() == MotionEvent::ACTION_DOWN) {
+  if (event.GetAction() == MotionEvent::Action::DOWN) {
     start_time_ = event.GetEventTime();
     start_touch_position_ = gfx::Point(event.GetX(), event.GetY());
     is_single_finger_ = true;
     max_distance_from_start_squared_ = 0;
-  } else if (event.GetAction() == MotionEvent::ACTION_MOVE &&
+  } else if (event.GetAction() == MotionEvent::Action::MOVE &&
              is_single_finger_) {
     float cur_dist = (start_touch_position_ -
                       gfx::Point(event.GetX(), event.GetY())).LengthSquared();
     if (cur_dist > max_distance_from_start_squared_)
       max_distance_from_start_squared_ = cur_dist;
   } else {
-    if (event.GetAction() == MotionEvent::ACTION_UP && is_single_finger_) {
+    if (event.GetAction() == MotionEvent::Action::UP && is_single_finger_) {
       UMA_HISTOGRAM_CUSTOM_COUNTS(
           "Event.TouchMaxDistance",
           static_cast<int>(sqrt(max_distance_from_start_squared_)),
diff --git a/ui/events/gesture_detection/motion_event.cc b/ui/events/gesture_detection/motion_event.cc
index 07ee9e1..a5c083b 100644
--- a/ui/events/gesture_detection/motion_event.cc
+++ b/ui/events/gesture_detection/motion_event.cc
@@ -59,4 +59,13 @@
   return MotionEventGeneric::CancelEvent(*this);
 }
 
+std::ostream& operator<<(std::ostream& stream,
+                         const MotionEvent::Action action) {
+  return stream << static_cast<int>(action);
+}
+std::ostream& operator<<(std::ostream& stream,
+                         const MotionEvent::ToolType tool_type) {
+  return stream << static_cast<int>(tool_type);
+}
+
 }  // namespace ui
diff --git a/ui/events/gesture_detection/motion_event.h b/ui/events/gesture_detection/motion_event.h
index 05a3c55..383b23a 100644
--- a/ui/events/gesture_detection/motion_event.h
+++ b/ui/events/gesture_detection/motion_event.h
@@ -19,30 +19,23 @@
 // subset of Android's MotionEvent API used in gesture detection.
 class GESTURE_DETECTION_EXPORT MotionEvent {
  public:
-  enum Action {
-    ACTION_NONE,
-    ACTION_DOWN,
-    ACTION_UP,
-    ACTION_MOVE,
-    ACTION_CANCEL,
-    ACTION_POINTER_DOWN,
-    ACTION_POINTER_UP,
-    ACTION_HOVER_ENTER,
-    ACTION_HOVER_EXIT,
-    ACTION_HOVER_MOVE,
-    ACTION_BUTTON_PRESS,
-    ACTION_BUTTON_RELEASE,
-    ACTION_LAST = ACTION_BUTTON_RELEASE
+  enum class Action {
+    NONE,
+    DOWN,
+    UP,
+    MOVE,
+    CANCEL,
+    POINTER_DOWN,
+    POINTER_UP,
+    HOVER_ENTER,
+    HOVER_EXIT,
+    HOVER_MOVE,
+    BUTTON_PRESS,
+    BUTTON_RELEASE,
+    LAST = BUTTON_RELEASE
   };
 
-  enum ToolType {
-    TOOL_TYPE_UNKNOWN,
-    TOOL_TYPE_FINGER,
-    TOOL_TYPE_STYLUS,
-    TOOL_TYPE_MOUSE,
-    TOOL_TYPE_ERASER,
-    TOOL_TYPE_LAST = TOOL_TYPE_ERASER
-  };
+  enum class ToolType { UNKNOWN, FINGER, STYLUS, MOUSE, ERASER, LAST = ERASER };
 
   enum ButtonType {
     BUTTON_PRIMARY = 1 << 0,
@@ -63,8 +56,8 @@
   // An unique identifier this motion event.
   virtual uint32_t GetUniqueEventId() const = 0;
   virtual Action GetAction() const = 0;
-  // Only valid if |GetAction()| returns ACTION_POINTER_UP or
-  // ACTION_POINTER_DOWN.
+  // Only valid if |GetAction()| returns Action::POINTER_UP or
+  // Action::POINTER_DOWN.
   virtual int GetActionIndex() const = 0;
   virtual size_t GetPointerCount() const = 0;
   virtual int GetPointerId(size_t pointer_index) const = 0;
@@ -139,6 +132,13 @@
   std::unique_ptr<MotionEvent> Cancel() const;
 };
 
+GESTURE_DETECTION_EXPORT std::ostream& operator<<(
+    std::ostream& stream,
+    const MotionEvent::Action action);
+GESTURE_DETECTION_EXPORT std::ostream& operator<<(
+    std::ostream& stream,
+    const MotionEvent::ToolType tool_type);
+
 }  // namespace ui
 
 #endif  // UI_EVENTS_GESTURE_DETECTION_MOTION_EVENT_H_
diff --git a/ui/events/gesture_detection/motion_event_buffer.cc b/ui/events/gesture_detection/motion_event_buffer.cc
index b2ce72f9..596ca394 100644
--- a/ui/events/gesture_detection/motion_event_buffer.cc
+++ b/ui/events/gesture_detection/motion_event_buffer.cc
@@ -36,8 +36,8 @@
 }
 
 bool CanAddSample(const MotionEvent& event0, const MotionEvent& event1) {
-  DCHECK_EQ(event0.GetAction(), MotionEvent::ACTION_MOVE);
-  if (event1.GetAction() != MotionEvent::ACTION_MOVE)
+  DCHECK_EQ(event0.GetAction(), MotionEvent::Action::MOVE);
+  if (event1.GetAction() != MotionEvent::Action::MOVE)
     return false;
 
   const size_t pointer_count = event0.GetPointerCount();
@@ -57,8 +57,8 @@
 }
 
 bool ShouldResampleTool(MotionEvent::ToolType tool) {
-  return tool == MotionEvent::TOOL_TYPE_UNKNOWN ||
-         tool == MotionEvent::TOOL_TYPE_FINGER;
+  return tool == MotionEvent::ToolType::UNKNOWN ||
+         tool == MotionEvent::ToolType::FINGER;
 }
 
 // Splits a chunk of events from the front of the provided |batch| and returns
@@ -109,7 +109,7 @@
     const MotionEvent& event0,
     const MotionEvent& event1,
     base::TimeTicks resample_time) {
-  DCHECK_EQ(MotionEvent::ACTION_MOVE, event0.GetAction());
+  DCHECK_EQ(MotionEvent::Action::MOVE, event0.GetAction());
   DCHECK_EQ(event0.GetPointerCount(), event1.GetPointerCount());
 
   const base::TimeTicks time0 = event0.GetEventTime();
@@ -130,8 +130,8 @@
         event0, event1, event0_i, static_cast<size_t>(event1_i), alpha);
 
     if (event0_i == 0) {
-      event.reset(new MotionEventGeneric(
-          MotionEvent::ACTION_MOVE, resample_time, pointer));
+      event.reset(new MotionEventGeneric(MotionEvent::Action::MOVE,
+                                         resample_time, pointer));
     } else {
       event->PushPointer(pointer);
     }
@@ -233,7 +233,7 @@
 
 void MotionEventBuffer::OnMotionEvent(const MotionEvent& event) {
   DCHECK_EQ(0U, event.GetHistorySize());
-  if (event.GetAction() != MotionEvent::ACTION_MOVE) {
+  if (event.GetAction() != MotionEvent::Action::MOVE) {
     last_extrapolated_event_time_ = base::TimeTicks();
     if (!buffered_events_.empty())
       FlushWithoutResampling(std::move(buffered_events_));
diff --git a/ui/events/gesture_detection/motion_event_buffer_unittest.cc b/ui/events/gesture_detection/motion_event_buffer_unittest.cc
index b8090b2..a6f7843 100644
--- a/ui/events/gesture_detection/motion_event_buffer_unittest.cc
+++ b/ui/events/gesture_detection/motion_event_buffer_unittest.cc
@@ -83,8 +83,8 @@
                                const MotionEvent& b,
                                bool ignore_history) {
     EXPECT_EQ(a.GetAction(), b.GetAction());
-    if (a.GetAction() == MotionEvent::ACTION_POINTER_DOWN ||
-        a.GetAction() == MotionEvent::ACTION_POINTER_UP) {
+    if (a.GetAction() == MotionEvent::Action::POINTER_DOWN ||
+        a.GetAction() == MotionEvent::Action::POINTER_UP) {
       EXPECT_EQ(a.GetActionIndex(), b.GetActionIndex());
     }
     EXPECT_EQ(a.GetButtonState(), b.GetButtonState());
@@ -185,8 +185,8 @@
     base::TimeDelta last_dt;
     while (event_time < max_event_time) {
       position += gfx::ScaleVector2d(velocity, event_time_delta.InSecondsF());
-      MockMotionEvent move(
-          MotionEvent::ACTION_MOVE, event_time, position.x(), position.y());
+      MockMotionEvent move(MotionEvent::Action::MOVE, event_time, position.x(),
+                           position.y());
       buffer.OnMotionEvent(move);
       event_time += event_time_delta;
 
@@ -258,7 +258,7 @@
   base::TimeTicks event_time = base::TimeTicks::Now();
   MotionEventBuffer buffer(this, true);
 
-  MockMotionEvent move(MotionEvent::ACTION_MOVE, event_time, 4.f, 4.f);
+  MockMotionEvent move(MotionEvent::Action::MOVE, event_time, 4.f, 4.f);
   buffer.OnMotionEvent(move);
   EXPECT_TRUE(GetAndResetNeedsFlush());
   EXPECT_FALSE(GetLastEvent());
@@ -274,7 +274,7 @@
   base::TimeTicks event_time = base::TimeTicks::Now();
   MotionEventBuffer buffer(this, true);
 
-  MockMotionEvent move0(MotionEvent::ACTION_MOVE, event_time, 1.f, 1.f);
+  MockMotionEvent move0(MotionEvent::Action::MOVE, event_time, 1.f, 1.f);
   buffer.OnMotionEvent(move0);
   EXPECT_TRUE(GetAndResetNeedsFlush());
   EXPECT_FALSE(GetLastEvent());
@@ -282,19 +282,19 @@
   event_time += base::TimeDelta::FromMilliseconds(5);
 
   // The second move should remain buffered.
-  MockMotionEvent move1(MotionEvent::ACTION_MOVE, event_time, 2.f, 2.f);
+  MockMotionEvent move1(MotionEvent::Action::MOVE, event_time, 2.f, 2.f);
   buffer.OnMotionEvent(move1);
   EXPECT_FALSE(GetAndResetNeedsFlush());
   EXPECT_FALSE(GetLastEvent());
 
   // The third move should remain buffered.
-  MockMotionEvent move2(MotionEvent::ACTION_MOVE, event_time, 3.f, 3.f);
+  MockMotionEvent move2(MotionEvent::Action::MOVE, event_time, 3.f, 3.f);
   buffer.OnMotionEvent(move2);
   EXPECT_FALSE(GetAndResetNeedsFlush());
   EXPECT_FALSE(GetLastEvent());
 
   // The up should flush the buffer.
-  MockMotionEvent up(MotionEvent::ACTION_UP, event_time, 4.f, 4.f);
+  MockMotionEvent up(MotionEvent::Action::UP, event_time, 4.f, 4.f);
   buffer.OnMotionEvent(up);
   EXPECT_FALSE(GetAndResetNeedsFlush());
 
@@ -314,7 +314,7 @@
   base::TimeTicks event_time = base::TimeTicks::Now();
   MotionEventBuffer buffer(this, true);
 
-  MockMotionEvent move0(MotionEvent::ACTION_MOVE, event_time, 1.f, 1.f);
+  MockMotionEvent move0(MotionEvent::Action::MOVE, event_time, 1.f, 1.f);
   buffer.OnMotionEvent(move0);
   EXPECT_TRUE(GetAndResetNeedsFlush());
   EXPECT_FALSE(GetLastEvent());
@@ -322,8 +322,8 @@
   event_time += base::TimeDelta::FromMilliseconds(5);
 
   // The second move has a different pointer count, flushing the first.
-  MockMotionEvent move1(
-      MotionEvent::ACTION_MOVE, event_time, 2.f, 2.f, 3.f, 3.f);
+  MockMotionEvent move1(MotionEvent::Action::MOVE, event_time, 2.f, 2.f, 3.f,
+                        3.f);
   buffer.OnMotionEvent(move1);
   EXPECT_FALSE(GetAndResetNeedsFlush());
   ASSERT_TRUE(GetLastEvent());
@@ -333,7 +333,7 @@
 
   // The third move has differing tool types, flushing the second.
   MockMotionEvent move2(move1);
-  move2.SetToolType(0, MotionEvent::TOOL_TYPE_STYLUS);
+  move2.SetToolType(0, MotionEvent::ToolType::STYLUS);
   buffer.OnMotionEvent(move2);
   EXPECT_FALSE(GetAndResetNeedsFlush());
   EXPECT_EVENT_EQ(move1, *GetLastEvent());
@@ -355,13 +355,13 @@
   pointer0.id = 1;
   PointerProperties pointer1(10.f, 10.f, 2.f);
   pointer1.id = 2;
-  MotionEventGeneric move3(MotionEvent::ACTION_MOVE, event_time, pointer0);
+  MotionEventGeneric move3(MotionEvent::Action::MOVE, event_time, pointer0);
   move3.PushPointer(pointer1);
   buffer.OnMotionEvent(move3);
   ASSERT_FALSE(GetLastEvent());
   EXPECT_TRUE(GetAndResetNeedsFlush());
 
-  MotionEventGeneric move4(MotionEvent::ACTION_MOVE, event_time, pointer0);
+  MotionEventGeneric move4(MotionEvent::Action::MOVE, event_time, pointer0);
   pointer1.id = 7;
   move4.PushPointer(pointer1);
   buffer.OnMotionEvent(move2);
@@ -374,7 +374,7 @@
   base::TimeTicks event_time = base::TimeTicks::Now();
   MotionEventBuffer buffer(this, true);
 
-  MockMotionEvent down(MotionEvent::ACTION_DOWN, event_time, 1.f, 1.f);
+  MockMotionEvent down(MotionEvent::Action::DOWN, event_time, 1.f, 1.f);
   buffer.OnMotionEvent(down);
   EXPECT_FALSE(GetAndResetNeedsFlush());
   ASSERT_TRUE(GetLastEvent());
@@ -382,7 +382,7 @@
 
   GetAndResetForwardedEvents();
 
-  MockMotionEvent up(MotionEvent::ACTION_UP, event_time, 2.f, 2.f);
+  MockMotionEvent up(MotionEvent::Action::UP, event_time, 2.f, 2.f);
   buffer.OnMotionEvent(up);
   EXPECT_FALSE(GetAndResetNeedsFlush());
   ASSERT_TRUE(GetLastEvent());
@@ -390,7 +390,7 @@
 
   GetAndResetForwardedEvents();
 
-  MockMotionEvent cancel(MotionEvent::ACTION_CANCEL, event_time, 3.f, 3.f);
+  MockMotionEvent cancel(MotionEvent::Action::CANCEL, event_time, 3.f, 3.f);
   buffer.OnMotionEvent(cancel);
   EXPECT_FALSE(GetAndResetNeedsFlush());
   ASSERT_TRUE(GetLastEvent());
@@ -398,7 +398,7 @@
 
   GetAndResetForwardedEvents();
 
-  MockMotionEvent move(MotionEvent::ACTION_MOVE, event_time, 4.f, 4.f);
+  MockMotionEvent move(MotionEvent::Action::MOVE, event_time, 4.f, 4.f);
   buffer.OnMotionEvent(move);
   EXPECT_TRUE(GetAndResetNeedsFlush());
   EXPECT_FALSE(GetLastEvent());
@@ -419,7 +419,7 @@
   PointerProperties p1(2.f, 1.f, 0.5f);
   p1.id = 2;
 
-  MotionEventGeneric move0(MotionEvent::ACTION_MOVE, event_time, p0);
+  MotionEventGeneric move0(MotionEvent::Action::MOVE, event_time, p0);
   move0.PushPointer(p1);
   buffer.OnMotionEvent(move0);
   EXPECT_TRUE(GetAndResetNeedsFlush());
@@ -429,7 +429,7 @@
 
   // The second move should remain buffered even if the logical pointers are
   // in a different order.
-  MotionEventGeneric move1(MotionEvent::ACTION_MOVE, event_time, p1);
+  MotionEventGeneric move1(MotionEvent::Action::MOVE, event_time, p1);
   move1.PushPointer(p0);
   buffer.OnMotionEvent(move1);
   EXPECT_FALSE(GetAndResetNeedsFlush());
@@ -452,14 +452,14 @@
   base::TimeTicks event_time = base::TimeTicks::Now();
   MotionEventBuffer buffer(this, true);
 
-  MockMotionEvent move0(MotionEvent::ACTION_MOVE, event_time, 1.f, 1.f);
+  MockMotionEvent move0(MotionEvent::Action::MOVE, event_time, 1.f, 1.f);
   buffer.OnMotionEvent(move0);
   ASSERT_FALSE(GetLastEvent());
   EXPECT_TRUE(GetAndResetNeedsFlush());
 
   // The second move should remain buffered.
   event_time += LargeDelta();
-  MockMotionEvent move1(MotionEvent::ACTION_MOVE, event_time, 2.f, 2.f);
+  MockMotionEvent move1(MotionEvent::Action::MOVE, event_time, 2.f, 2.f);
   buffer.OnMotionEvent(move1);
   ASSERT_FALSE(GetLastEvent());
   EXPECT_FALSE(GetAndResetNeedsFlush());
@@ -509,13 +509,13 @@
   MotionEventBuffer buffer(this, resampling_enabled);
 
   // Queue two events.
-  MockMotionEvent move0(MotionEvent::ACTION_MOVE, event_time, 5.f, 10.f);
+  MockMotionEvent move0(MotionEvent::Action::MOVE, event_time, 5.f, 10.f);
   buffer.OnMotionEvent(move0);
   ASSERT_FALSE(GetLastEvent());
   EXPECT_TRUE(GetAndResetNeedsFlush());
 
   event_time += base::TimeDelta::FromMilliseconds(5);
-  MockMotionEvent move1(MotionEvent::ACTION_MOVE, event_time, 15.f, 30.f);
+  MockMotionEvent move1(MotionEvent::Action::MOVE, event_time, 15.f, 30.f);
   buffer.OnMotionEvent(move1);
   ASSERT_FALSE(GetLastEvent());
   EXPECT_FALSE(GetAndResetNeedsFlush());
@@ -543,14 +543,14 @@
   GetAndResetForwardedEvents();
 
   // Now queue two more events.
-  move0 = MockMotionEvent(MotionEvent::ACTION_MOVE, event_time, 5.f, 10.f);
+  move0 = MockMotionEvent(MotionEvent::Action::MOVE, event_time, 5.f, 10.f);
   buffer.OnMotionEvent(move0);
   ASSERT_FALSE(GetLastEvent());
   EXPECT_TRUE(GetAndResetNeedsFlush());
 
   // The second move should remain buffered.
   event_time += base::TimeDelta::FromMilliseconds(5);
-  move1 = MockMotionEvent(MotionEvent::ACTION_MOVE, event_time, 10.f, 20.f);
+  move1 = MockMotionEvent(MotionEvent::Action::MOVE, event_time, 10.f, 20.f);
   buffer.OnMotionEvent(move1);
   ASSERT_FALSE(GetLastEvent());
   EXPECT_FALSE(GetAndResetNeedsFlush());
@@ -575,14 +575,14 @@
   base::TimeTicks event_time = base::TimeTicks::Now();
   MotionEventBuffer buffer(this, true);
 
-  MockMotionEvent move0(MotionEvent::ACTION_MOVE, event_time, 5.f, 10.f);
+  MockMotionEvent move0(MotionEvent::Action::MOVE, event_time, 5.f, 10.f);
   buffer.OnMotionEvent(move0);
   ASSERT_FALSE(GetLastEvent());
   EXPECT_TRUE(GetAndResetNeedsFlush());
 
   // The second move should remain buffered.
   event_time += base::TimeDelta::FromMilliseconds(10);
-  MockMotionEvent move1(MotionEvent::ACTION_MOVE, event_time, 10.f, 20.f);
+  MockMotionEvent move1(MotionEvent::Action::MOVE, event_time, 10.f, 20.f);
   buffer.OnMotionEvent(move1);
   ASSERT_FALSE(GetLastEvent());
   EXPECT_FALSE(GetAndResetNeedsFlush());
@@ -609,14 +609,14 @@
   // Try enqueuing an event *after* the second event but *before* the
   // extrapolated event. It should be dropped.
   event_time = move1.GetEventTime() + base::TimeDelta::FromMilliseconds(1);
-  MockMotionEvent move2(MotionEvent::ACTION_MOVE, event_time, 15.f, 25.f);
+  MockMotionEvent move2(MotionEvent::Action::MOVE, event_time, 15.f, 25.f);
   buffer.OnMotionEvent(move1);
   ASSERT_FALSE(GetLastEvent());
   EXPECT_FALSE(GetAndResetNeedsFlush());
 
   // Finally queue an event *after* the extrapolated event.
   event_time = expected_time + base::TimeDelta::FromMilliseconds(1);
-  MockMotionEvent move3(MotionEvent::ACTION_MOVE, event_time, 15.f, 25.f);
+  MockMotionEvent move3(MotionEvent::Action::MOVE, event_time, 15.f, 25.f);
   buffer.OnMotionEvent(move3);
   ASSERT_FALSE(GetLastEvent());
   EXPECT_TRUE(GetAndResetNeedsFlush());
@@ -637,14 +637,14 @@
   MotionEventBuffer buffer(this, true);
 
   // The first move should be buffered.
-  MockMotionEvent move0(MotionEvent::ACTION_MOVE, event_time, 1.f, 1.f);
+  MockMotionEvent move0(MotionEvent::Action::MOVE, event_time, 1.f, 1.f);
   buffer.OnMotionEvent(move0);
   ASSERT_FALSE(GetLastEvent());
   EXPECT_TRUE(GetAndResetNeedsFlush());
 
   // The second move should remain buffered.
   event_time += SmallDelta();
-  MockMotionEvent move1(MotionEvent::ACTION_MOVE, event_time, 2.f, 2.f);
+  MockMotionEvent move1(MotionEvent::Action::MOVE, event_time, 2.f, 2.f);
   buffer.OnMotionEvent(move1);
   ASSERT_FALSE(GetLastEvent());
   EXPECT_FALSE(GetAndResetNeedsFlush());
@@ -668,14 +668,14 @@
   MotionEventBuffer buffer(this, true);
 
   // The first move should be buffered.
-  MockMotionEvent move0(MotionEvent::ACTION_MOVE, event_time, 1.f, 1.f);
+  MockMotionEvent move0(MotionEvent::Action::MOVE, event_time, 1.f, 1.f);
   buffer.OnMotionEvent(move0);
   ASSERT_FALSE(GetLastEvent());
   EXPECT_TRUE(GetAndResetNeedsFlush());
 
   // The second move should remain buffered.
   event_time += SmallDelta();
-  MockMotionEvent move1(MotionEvent::ACTION_MOVE, event_time, 2.f, 2.f);
+  MockMotionEvent move1(MotionEvent::Action::MOVE, event_time, 2.f, 2.f);
   buffer.OnMotionEvent(move1);
   ASSERT_FALSE(GetLastEvent());
   EXPECT_FALSE(GetAndResetNeedsFlush());
@@ -698,14 +698,14 @@
   base::TimeTicks event_time = base::TimeTicks::Now();
   MotionEventBuffer buffer(this, true);
 
-  MockMotionEvent move0(MotionEvent::ACTION_MOVE, event_time, 5.f, 10.f);
+  MockMotionEvent move0(MotionEvent::Action::MOVE, event_time, 5.f, 10.f);
   buffer.OnMotionEvent(move0);
   ASSERT_FALSE(GetLastEvent());
   EXPECT_TRUE(GetAndResetNeedsFlush());
 
   // The second move should remain buffered.
   event_time += base::TimeDelta::FromMilliseconds(5);
-  MockMotionEvent move1(MotionEvent::ACTION_MOVE, event_time, 15.f, 30.f);
+  MockMotionEvent move1(MotionEvent::Action::MOVE, event_time, 15.f, 30.f);
   buffer.OnMotionEvent(move1);
   ASSERT_FALSE(GetLastEvent());
   EXPECT_FALSE(GetAndResetNeedsFlush());
@@ -723,8 +723,7 @@
   float alpha = (interpolated_time - move0.GetEventTime()).InMillisecondsF() /
                 (move1.GetEventTime() - move0.GetEventTime()).InMillisecondsF();
   MockMotionEvent interpolated_event(
-      MotionEvent::ACTION_MOVE,
-      interpolated_time,
+      MotionEvent::Action::MOVE, interpolated_time,
       move0.GetX(0) + (move1.GetX(0) - move0.GetX(0)) * alpha,
       move0.GetY(0) + (move1.GetY(0) - move0.GetY(0)) * alpha);
   std::vector<std::unique_ptr<MotionEvent>> events =
@@ -745,14 +744,14 @@
   base::TimeTicks event_time = base::TimeTicks::Now();
   MotionEventBuffer buffer(this, true);
 
-  MockMotionEvent move0(MotionEvent::ACTION_MOVE, event_time, 5.f, 10.f);
+  MockMotionEvent move0(MotionEvent::Action::MOVE, event_time, 5.f, 10.f);
   buffer.OnMotionEvent(move0);
   ASSERT_FALSE(GetLastEvent());
   EXPECT_TRUE(GetAndResetNeedsFlush());
 
   // The second move should remain buffered.
   event_time += base::TimeDelta::FromMilliseconds(5);
-  MockMotionEvent move1(MotionEvent::ACTION_MOVE, event_time, 10.f, 20.f);
+  MockMotionEvent move1(MotionEvent::Action::MOVE, event_time, 10.f, 20.f);
   buffer.OnMotionEvent(move1);
   ASSERT_FALSE(GetLastEvent());
   EXPECT_FALSE(GetAndResetNeedsFlush());
@@ -776,8 +775,7 @@
       (expected_time - move0.GetEventTime()).InMillisecondsF() /
       (move1.GetEventTime() - move0.GetEventTime()).InMillisecondsF();
   MockMotionEvent extrapolated_event(
-      MotionEvent::ACTION_MOVE,
-      expected_time,
+      MotionEvent::Action::MOVE, expected_time,
       move0.GetX(0) + (move1.GetX(0) - move0.GetX(0)) * expected_alpha,
       move0.GetY(0) + (move1.GetY(0) - move0.GetY(0)) * expected_alpha);
   std::vector<std::unique_ptr<MotionEvent>> events =
@@ -793,14 +791,14 @@
   base::TimeTicks event_time = base::TimeTicks::Now();
   MotionEventBuffer buffer(this, true);
 
-  MockMotionEvent move0(MotionEvent::ACTION_MOVE, event_time, 5.f, 10.f);
+  MockMotionEvent move0(MotionEvent::Action::MOVE, event_time, 5.f, 10.f);
   buffer.OnMotionEvent(move0);
   ASSERT_FALSE(GetLastEvent());
   EXPECT_TRUE(GetAndResetNeedsFlush());
 
   // The second move should remain buffered.
   event_time += base::TimeDelta::FromMilliseconds(24);
-  MockMotionEvent move1(MotionEvent::ACTION_MOVE, event_time, 10.f, 20.f);
+  MockMotionEvent move1(MotionEvent::Action::MOVE, event_time, 10.f, 20.f);
   buffer.OnMotionEvent(move1);
   ASSERT_FALSE(GetLastEvent());
   EXPECT_FALSE(GetAndResetNeedsFlush());
@@ -822,8 +820,7 @@
       (expected_time - move0.GetEventTime()).InMillisecondsF() /
       (move1.GetEventTime() - move0.GetEventTime()).InMillisecondsF();
   MockMotionEvent extrapolated_event(
-      MotionEvent::ACTION_MOVE,
-      expected_time,
+      MotionEvent::Action::MOVE, expected_time,
       move0.GetX(0) + (move1.GetX(0) - move0.GetX(0)) * expected_alpha,
       move0.GetY(0) + (move1.GetY(0) - move0.GetY(0)) * expected_alpha);
   std::vector<std::unique_ptr<MotionEvent>> events =
diff --git a/ui/events/gesture_detection/motion_event_generic.cc b/ui/events/gesture_detection/motion_event_generic.cc
index 07afcd6..293ea0b 100644
--- a/ui/events/gesture_detection/motion_event_generic.cc
+++ b/ui/events/gesture_detection/motion_event_generic.cc
@@ -20,7 +20,7 @@
 
 PointerProperties::PointerProperties(float x, float y, float touch_major)
     : id(0),
-      tool_type(MotionEvent::TOOL_TYPE_UNKNOWN),
+      tool_type(MotionEvent::ToolType::UNKNOWN),
       x(x),
       y(y),
       raw_x(x),
@@ -119,7 +119,7 @@
 }
 
 int MotionEventGeneric::GetActionIndex() const {
-  DCHECK(action_ == ACTION_POINTER_DOWN || action_ == ACTION_POINTER_UP);
+  DCHECK(action_ == Action::POINTER_DOWN || action_ == Action::POINTER_UP);
   DCHECK_GE(action_index_, 0);
   DCHECK_LT(action_index_, static_cast<int>(pointers_->size()));
   return action_index_;
@@ -244,7 +244,7 @@
   bool with_history = false;
   std::unique_ptr<MotionEventGeneric> cancel_event(
       new MotionEventGeneric(event, with_history));
-  cancel_event->set_action(ACTION_CANCEL);
+  cancel_event->set_action(Action::CANCEL);
   cancel_event->set_unique_event_id(ui::GetNextTouchEventId());
   return cancel_event;
 }
@@ -263,7 +263,7 @@
 void MotionEventGeneric::PushHistoricalEvent(
     std::unique_ptr<MotionEvent> event) {
   DCHECK(event);
-  DCHECK_EQ(event->GetAction(), ACTION_MOVE);
+  DCHECK_EQ(event->GetAction(), Action::MOVE);
   DCHECK_EQ(event->GetPointerCount(), GetPointerCount());
   DCHECK_EQ(event->GetAction(), GetAction());
   DCHECK_LE(event->GetEventTime(), GetEventTime());
@@ -271,11 +271,10 @@
 }
 
 MotionEventGeneric::MotionEventGeneric()
-    : action_(ACTION_NONE),
+    : action_(Action::NONE),
       unique_event_id_(ui::GetNextTouchEventId()),
       action_index_(-1),
-      button_state_(0) {
-}
+      button_state_(0) {}
 
 MotionEventGeneric::MotionEventGeneric(const MotionEvent& event,
                                        bool with_history)
@@ -283,7 +282,7 @@
       event_time_(event.GetEventTime()),
       unique_event_id_(event.GetUniqueEventId()),
       action_index_(
-          (action_ == ACTION_POINTER_UP || action_ == ACTION_POINTER_DOWN)
+          (action_ == Action::POINTER_UP || action_ == Action::POINTER_DOWN)
               ? event.GetActionIndex()
               : 0),
       button_state_(event.GetButtonState()),
@@ -299,7 +298,7 @@
   for (size_t h = 0; h < history_size; ++h) {
     std::unique_ptr<MotionEventGeneric> historical_event(
         new MotionEventGeneric());
-    historical_event->set_action(ACTION_MOVE);
+    historical_event->set_action(Action::MOVE);
     historical_event->set_event_time(event.GetHistoricalEventTime(h));
     for (size_t i = 0; i < pointer_count; ++i) {
       historical_event->PushPointer(
diff --git a/ui/events/gesture_detection/motion_event_generic_unittest.cc b/ui/events/gesture_detection/motion_event_generic_unittest.cc
index 0f5b2c5e..c1eb776 100644
--- a/ui/events/gesture_detection/motion_event_generic_unittest.cc
+++ b/ui/events/gesture_detection/motion_event_generic_unittest.cc
@@ -15,8 +15,8 @@
 
 TEST(MotionEventGenericTest, Basic) {
   base::TimeTicks event_time = base::TimeTicks::Now();
-  MotionEventGeneric event(
-      MotionEvent::ACTION_DOWN, event_time, PointerProperties());
+  MotionEventGeneric event(MotionEvent::Action::DOWN, event_time,
+                           PointerProperties());
   EXPECT_EQ(1U, event.GetPointerCount());
   EXPECT_EQ(0U, event.GetHistorySize());
   EXPECT_EQ(event_time, event.GetEventTime());
@@ -38,8 +38,8 @@
   event.pointer(0).id = 3;
   EXPECT_EQ(3, event.GetPointerId(0));
 
-  event.set_action(MotionEvent::ACTION_POINTER_DOWN);
-  EXPECT_EQ(MotionEvent::ACTION_POINTER_DOWN, event.GetAction());
+  event.set_action(MotionEvent::Action::POINTER_DOWN);
+  EXPECT_EQ(MotionEvent::Action::POINTER_DOWN, event.GetAction());
 
   event_time += base::TimeDelta::FromMilliseconds(5);
   event.set_event_time(event_time);
@@ -54,16 +54,15 @@
   event.set_action_index(1);
   EXPECT_EQ(1, event.GetActionIndex());
 
-  event.set_action(MotionEvent::ACTION_MOVE);
-  EXPECT_EQ(MotionEvent::ACTION_MOVE, event.GetAction());
+  event.set_action(MotionEvent::Action::MOVE);
+  EXPECT_EQ(MotionEvent::Action::MOVE, event.GetAction());
 
   PointerProperties historical_pointer0(1.2f, 2.4f, 1.f);
   PointerProperties historical_pointer1(2.4f, 4.8f, 2.f);
   PointerProperties historical_pointer2(4.8f, 9.6f, 3.f);
   MotionEventGeneric historical_event(
-      MotionEvent::ACTION_MOVE,
-      event_time - base::TimeDelta::FromMilliseconds(5),
-      historical_pointer0);
+      MotionEvent::Action::MOVE,
+      event_time - base::TimeDelta::FromMilliseconds(5), historical_pointer0);
   historical_event.PushPointer(historical_pointer1);
   historical_event.PushPointer(historical_pointer2);
 
@@ -83,8 +82,7 @@
 }
 
 TEST(MotionEventGenericTest, Clone) {
-  MotionEventGeneric event(MotionEvent::ACTION_DOWN,
-                           base::TimeTicks::Now(),
+  MotionEventGeneric event(MotionEvent::Action::DOWN, base::TimeTicks::Now(),
                            PointerProperties(8.3f, 4.7f, 2.f));
   event.set_button_state(MotionEvent::BUTTON_PRIMARY);
 
@@ -100,11 +98,11 @@
       event_time - base::TimeDelta::FromMilliseconds(5);
 
   PointerProperties pointer(8.3f, 4.7f, 10.1f);
-  MotionEventGeneric event(MotionEvent::ACTION_MOVE, event_time, pointer);
+  MotionEventGeneric event(MotionEvent::Action::MOVE, event_time, pointer);
 
   PointerProperties historical_pointer(3.4f, -4.3f, 11.5);
   std::unique_ptr<MotionEvent> historical_event(new MotionEventGeneric(
-      MotionEvent::ACTION_MOVE, historical_event_time, historical_pointer));
+      MotionEvent::Action::MOVE, historical_event_time, historical_pointer));
 
   event.PushHistoricalEvent(std::move(historical_event));
   EXPECT_EQ(1U, event.GetHistorySize());
@@ -116,13 +114,12 @@
 }
 
 TEST(MotionEventGenericTest, Cancel) {
-  MotionEventGeneric event(MotionEvent::ACTION_UP,
-                           base::TimeTicks::Now(),
+  MotionEventGeneric event(MotionEvent::Action::UP, base::TimeTicks::Now(),
                            PointerProperties(8.7f, 4.3f, 1.f));
   event.set_button_state(MotionEvent::BUTTON_SECONDARY);
 
   std::unique_ptr<MotionEvent> cancel = event.Cancel();
-  event.set_action(MotionEvent::ACTION_CANCEL);
+  event.set_action(MotionEvent::Action::CANCEL);
   ASSERT_TRUE(cancel);
   EXPECT_NE(event.GetUniqueEventId(), cancel->GetUniqueEventId());
   EXPECT_EQ(test::ToString(event), test::ToString(*cancel));
@@ -132,7 +129,7 @@
   base::TimeTicks event_time = base::TimeTicks::Now();
   PointerProperties pointer;
   pointer.id = 0;
-  MotionEventGeneric event0(MotionEvent::ACTION_DOWN, event_time, pointer);
+  MotionEventGeneric event0(MotionEvent::Action::DOWN, event_time, pointer);
   EXPECT_EQ(0, event0.FindPointerIndexOfId(0));
   EXPECT_EQ(-1, event0.FindPointerIndexOfId(1));
   EXPECT_EQ(-1, event0.FindPointerIndexOfId(-1));
@@ -159,7 +156,7 @@
   base::TimeTicks event_time = base::TimeTicks::Now();
   PointerProperties pointer;
   pointer.id = 0;
-  MotionEventGeneric event(MotionEvent::ACTION_DOWN, event_time, pointer);
+  MotionEventGeneric event(MotionEvent::Action::DOWN, event_time, pointer);
 
   pointer.id = 7;
   EXPECT_EQ(1u, event.PushPointer(pointer));
@@ -245,19 +242,19 @@
   pointer0.touch_major = 35;
   pointer0.orientation = -1;
 
-  MotionEventGeneric event(MotionEvent::ACTION_MOVE, event_time, pointer0);
+  MotionEventGeneric event(MotionEvent::Action::MOVE, event_time, pointer0);
   event.PushPointer(pointer1);
 
   pointer0.x += 50;
   pointer1.x -= 50;
   std::unique_ptr<MotionEventGeneric> historical_event0(new MotionEventGeneric(
-      MotionEvent::ACTION_MOVE, historical_event_time0, pointer0));
+      MotionEvent::Action::MOVE, historical_event_time0, pointer0));
   historical_event0->PushPointer(pointer1);
 
   pointer0.x += 100;
   pointer1.x -= 100;
   std::unique_ptr<MotionEventGeneric> historical_event1(new MotionEventGeneric(
-      MotionEvent::ACTION_MOVE, historical_event_time1, pointer0));
+      MotionEvent::Action::MOVE, historical_event_time1, pointer0));
   historical_event1->PushPointer(pointer1);
 
   event.PushHistoricalEvent(std::move(historical_event0));
diff --git a/ui/events/gesture_detection/scale_gesture_detector.cc b/ui/events/gesture_detection/scale_gesture_detector.cc
index 73beab4..eace042 100644
--- a/ui/events/gesture_detection/scale_gesture_detector.cc
+++ b/ui/events/gesture_detection/scale_gesture_detector.cc
@@ -71,7 +71,7 @@
 bool ScaleGestureDetector::OnTouchEvent(const MotionEvent& event) {
   curr_time_ = event.GetEventTime();
 
-  const int action = event.GetAction();
+  const MotionEvent::Action action = event.GetAction();
 
   const int count = static_cast<int>(event.GetPointerCount());
   const bool is_stylus_button_down =
@@ -82,11 +82,11 @@
       !is_stylus_button_down;
 
   const bool stream_complete =
-      action == MotionEvent::ACTION_UP ||
-      action == MotionEvent::ACTION_CANCEL || anchored_scale_cancelled ||
-      (action == MotionEvent::ACTION_POINTER_DOWN && InAnchoredScaleMode());
+      action == MotionEvent::Action::UP ||
+      action == MotionEvent::Action::CANCEL || anchored_scale_cancelled ||
+      (action == MotionEvent::Action::POINTER_DOWN && InAnchoredScaleMode());
 
-  if (action == MotionEvent::ACTION_DOWN || stream_complete) {
+  if (action == MotionEvent::Action::DOWN || stream_complete) {
     // Reset any scale in progress with the listener.
     // If it's an ACTION_DOWN we're beginning a new event stream.
     // This means the app probably didn't give us all the events. Shame on it.
@@ -110,12 +110,12 @@
     initial_span_ = 0;
   }
 
-  const bool config_changed = action == MotionEvent::ACTION_DOWN ||
-                              action == MotionEvent::ACTION_POINTER_UP ||
-                              action == MotionEvent::ACTION_POINTER_DOWN ||
+  const bool config_changed = action == MotionEvent::Action::DOWN ||
+                              action == MotionEvent::Action::POINTER_UP ||
+                              action == MotionEvent::Action::POINTER_DOWN ||
                               anchored_scale_cancelled;
 
-  const bool pointer_up = action == MotionEvent::ACTION_POINTER_UP;
+  const bool pointer_up = action == MotionEvent::Action::POINTER_UP;
   const int skip_index = pointer_up ? event.GetActionIndex() : -1;
 
   // Determine focal point.
@@ -210,7 +210,7 @@
   }
 
   // Handle motion; focal point and span/scale factor are changing.
-  if (action == MotionEvent::ACTION_MOVE) {
+  if (action == MotionEvent::Action::MOVE) {
     curr_span_x_ = span_x;
     curr_span_y_ = span_y;
     curr_span_ = span;
diff --git a/ui/events/gesture_detection/scale_gesture_detector.h b/ui/events/gesture_detection/scale_gesture_detector.h
index 38038bd..9a407854 100644
--- a/ui/events/gesture_detection/scale_gesture_detector.h
+++ b/ui/events/gesture_detection/scale_gesture_detector.h
@@ -50,8 +50,8 @@
   //
   // Note: Applications should pass a complete and consistent event stream to
   // this method. A complete and consistent event stream involves all
-  // MotionEvents from the initial ACTION_DOWN to the final ACTION_UP or
-  // ACTION_CANCEL.
+  // MotionEvents from the initial Action::DOWN to the final Action::UP or
+  // Action::CANCEL.
   //
   // Returns true if the event was processed and the detector wants to receive
   // the rest of the MotionEvents in this event stream.
diff --git a/ui/events/gesture_detection/snap_scroll_controller.cc b/ui/events/gesture_detection/snap_scroll_controller.cc
index b50971d3..a393c2e 100644
--- a/ui/events/gesture_detection/snap_scroll_controller.cc
+++ b/ui/events/gesture_detection/snap_scroll_controller.cc
@@ -51,12 +51,12 @@
     const MotionEvent& event,
     bool is_scale_gesture_detection_in_progress) {
   switch (event.GetAction()) {
-    case MotionEvent::ACTION_DOWN:
+    case MotionEvent::Action::DOWN:
       mode_ = SNAP_PENDING;
       down_position_.set_x(event.GetX());
       down_position_.set_y(event.GetY());
       break;
-    case MotionEvent::ACTION_MOVE: {
+    case MotionEvent::Action::MOVE: {
       if (is_scale_gesture_detection_in_progress)
         break;
 
@@ -80,8 +80,8 @@
       if (mode_ == SNAP_PENDING && dx > kMaxSnapBound && dy > kMaxSnapBound)
         mode_ = SNAP_NONE;
     } break;
-    case MotionEvent::ACTION_UP:
-    case MotionEvent::ACTION_CANCEL:
+    case MotionEvent::Action::UP:
+    case MotionEvent::Action::CANCEL:
       down_position_ = gfx::PointF();
       accumulated_distance_ = gfx::Vector2dF();
       break;
diff --git a/ui/events/gesture_detection/touch_disposition_gesture_filter.cc b/ui/events/gesture_detection/touch_disposition_gesture_filter.cc
index a23bc35..4490825 100644
--- a/ui/events/gesture_detection/touch_disposition_gesture_filter.cc
+++ b/ui/events/gesture_detection/touch_disposition_gesture_filter.cc
@@ -139,7 +139,7 @@
     TouchDispositionGestureFilterClient* client)
     : client_(client),
       ending_event_motion_event_id_(0),
-      ending_event_primary_tool_type_(MotionEvent::TOOL_TYPE_UNKNOWN),
+      ending_event_primary_tool_type_(MotionEvent::ToolType::UNKNOWN),
       needs_tap_ending_event_(false),
       needs_show_press_event_(false),
       needs_fling_ending_event_(false),
diff --git a/ui/events/gesture_detection/touch_disposition_gesture_filter_unittest.cc b/ui/events/gesture_detection/touch_disposition_gesture_filter_unittest.cc
index 613cc75..c2c26616 100644
--- a/ui/events/gesture_detection/touch_disposition_gesture_filter_unittest.cc
+++ b/ui/events/gesture_detection/touch_disposition_gesture_filter_unittest.cc
@@ -275,7 +275,7 @@
     GestureEventDetails details(type);
     details.set_device_type(GestureDeviceType::DEVICE_TOUCHSCREEN);
     return GestureEventData(
-        details, 0, MotionEvent::TOOL_TYPE_FINGER, base::TimeTicks(), x, y, 0,
+        details, 0, MotionEvent::ToolType::FINGER, base::TimeTicks(), x, y, 0,
         0, 1,
         gfx::RectF(x - diameter / 2, y - diameter / 2, diameter, diameter),
         kDefaultEventFlags, 0U);
diff --git a/ui/events/gesture_detection/velocity_tracker.cc b/ui/events/gesture_detection/velocity_tracker.cc
index 4094525..b0f98402 100644
--- a/ui/events/gesture_detection/velocity_tracker.cc
+++ b/ui/events/gesture_detection/velocity_tracker.cc
@@ -36,16 +36,16 @@
 
 static_assert(MotionEvent::MAX_POINTER_ID < 32, "max pointer id too large");
 
-// Threshold between ACTION_MOVE events for determining that a pointer has
-// stopped moving. Some input devices do not send ACTION_MOVE events in the case
-// where a pointer has stopped.  We need to detect this case so that we can
+// Threshold between Action::MOVE events for determining that a pointer has
+// stopped moving. Some input devices do not send Action::MOVE events in the
+// case where a pointer has stopped.  We need to detect this case so that we can
 // accurately predict the velocity after the pointer starts moving again.
 const int kAssumePointerMoveStoppedTimeMs = 40;
 
-// Threshold between ACTION_MOVE and ACTION_{UP|POINTER_UP} events for
+// Threshold between Action::MOVE and Action::{UP|POINTER_UP} events for
 // determining that a pointer has stopped moving. This is a larger threshold
 // than |kAssumePointerMoveStoppedTimeMs|, as some devices may delay synthesis
-// of ACTION_{UP|POINTER_UP} to reduce risk of noisy release.
+// of Action::{UP|POINTER_UP} to reduce risk of noisy release.
 const int kAssumePointerUpStoppedTimeMs = 80;
 
 struct Position {
@@ -297,15 +297,13 @@
 }
 
 void VelocityTracker::AddMovement(const MotionEvent& event) {
-  int32_t actionMasked = event.GetAction();
-
-  switch (actionMasked) {
-    case MotionEvent::ACTION_DOWN:
+  switch (event.GetAction()) {
+    case MotionEvent::Action::DOWN:
       // case MotionEvent::HOVER_ENTER:
       // Clear all pointers on down before adding the new movement.
       Clear();
       break;
-    case MotionEvent::ACTION_POINTER_DOWN: {
+    case MotionEvent::Action::POINTER_DOWN: {
       // Start a new movement trace for a pointer that just went down.
       // We do this on down instead of on up because the client may want to
       // query the final velocity for a pointer that just went up.
@@ -314,20 +312,20 @@
       ClearPointers(downIdBits);
       break;
     }
-    case MotionEvent::ACTION_MOVE:
-      // case MotionEvent::ACTION_HOVER_MOVE:
+    case MotionEvent::Action::MOVE:
+      // case MotionEvent::Action::HOVER_MOVE:
       break;
-    case MotionEvent::ACTION_UP:
-    case MotionEvent::ACTION_POINTER_UP:
-      // Note that ACTION_UP and ACTION_POINTER_UP always report the last known
-      // position of the pointers that went up.  ACTION_POINTER_UP does include
-      // the new position of pointers that remained down but we will also
-      // receive an ACTION_MOVE with this information if any of them actually
-      // moved.  Since we don't know how many pointers will be going up at once
-      // it makes sense to just wait for the following ACTION_MOVE before adding
-      // the movement. However, if the up event itself is delayed because of
-      // (difficult albeit possible) prolonged stationary screen contact, assume
-      // that motion has stopped.
+    case MotionEvent::Action::UP:
+    case MotionEvent::Action::POINTER_UP:
+      // Note that Action::UP and Action::POINTER_UP always report the last
+      // known position of the pointers that went up.  Action::POINTER_UP does
+      // include the new position of pointers that remained down but we will
+      // also receive an Action::MOVE with this information if any of them
+      // actually moved.  Since we don't know how many pointers will be going up
+      // at once it makes sense to just wait for the following Action::MOVE
+      // before adding the movement. However, if the up event itself is delayed
+      // because of (difficult albeit possible) prolonged stationary screen
+      // contact, assume that motion has stopped.
       if ((event.GetEventTime() - last_event_time_) >=
           base::TimeDelta::FromMilliseconds(kAssumePointerUpStoppedTimeMs))
         strategy_->Clear();
diff --git a/ui/events/gesture_detection/velocity_tracker_unittest.cc b/ui/events/gesture_detection/velocity_tracker_unittest.cc
index a8e2880..e828d351 100644
--- a/ui/events/gesture_detection/velocity_tracker_unittest.cc
+++ b/ui/events/gesture_detection/velocity_tracker_unittest.cc
@@ -68,9 +68,9 @@
     if (!samples)
       return;
     const base::TimeDelta dt = t / samples;
-    state->AddMovement(Sample(MotionEvent::ACTION_DOWN, p0, t0, v, dt * 0));
+    state->AddMovement(Sample(MotionEvent::Action::DOWN, p0, t0, v, dt * 0));
     ApplyMovement(state, p0, v, t0, t, samples);
-    state->AddMovement(Sample(MotionEvent::ACTION_UP, p0, t0, v, t));
+    state->AddMovement(Sample(MotionEvent::Action::UP, p0, t0, v, t));
   }
 
   static void ApplyMovement(VelocityTrackerState* state,
@@ -84,7 +84,7 @@
       return;
     const base::TimeDelta dt = t / samples;
     for (size_t i = 0; i < samples; ++i)
-      state->AddMovement(Sample(MotionEvent::ACTION_MOVE, p0, t0, v, dt * i));
+      state->AddMovement(Sample(MotionEvent::Action::MOVE, p0, t0, v, dt * i));
   }
 };
 
@@ -159,7 +159,7 @@
     base::TimeTicks t0 = base::TimeTicks::Now();
     base::TimeDelta dt = kTenMillis * 10;
     state.AddMovement(
-        Sample(MotionEvent::ACTION_DOWN, p0, t0, vFast, base::TimeDelta()));
+        Sample(MotionEvent::Action::DOWN, p0, t0, vFast, base::TimeDelta()));
 
     // Apply some fast movement and compute the velocity.
     gfx::PointF pCurr = p0;
@@ -202,7 +202,7 @@
 
   VelocityTrackerState state(VelocityTracker::Strategy::LSQ2);
   state.AddMovement(
-      Sample(MotionEvent::ACTION_DOWN, p0, t0, v, base::TimeDelta()));
+      Sample(MotionEvent::Action::DOWN, p0, t0, v, base::TimeDelta()));
 
   // Apply the movement and verify a (non-zero) velocity.
   ApplyMovement(&state, p0, v, t0, dt, samples);
@@ -210,11 +210,11 @@
   EXPECT_NEAR(-1000, state.GetXVelocity(0), kEpsilson);
   EXPECT_NEAR(1000, state.GetYVelocity(0), kEpsilson);
 
-  // Apply the delayed ACTION_UP.
+  // Apply the delayed Action::UP.
   const gfx::PointF p1 = p0 + ScaleVector2d(v, dt.InSecondsF());
   const base::TimeTicks t1 = t0 + dt + kTenMillis * 10;
-  state.AddMovement(Sample(
-      MotionEvent::ACTION_UP, p1, t1, v, base::TimeDelta()));
+  state.AddMovement(
+      Sample(MotionEvent::Action::UP, p1, t1, v, base::TimeDelta()));
 
   // The tracked velocity should have been reset.
   state.ComputeCurrentVelocity(1000, 1000);
@@ -234,14 +234,14 @@
 
   gfx::PointF p(0, 0);
 
-  MockMotionEvent m1(MotionEvent::ACTION_DOWN, t0, p.x(), p.y());
+  MockMotionEvent m1(MotionEvent::Action::DOWN, t0, p.x(), p.y());
   state_unrestricted.AddMovement(m1);
   state_restricted.AddMovement(m1);
 
   for (size_t i = 0; i < samples; ++i) {
     if (i < 50)
       p.set_y(p.y() + 10);
-    MockMotionEvent mi(MotionEvent::ACTION_MOVE, t0 + dt * i, p.x(), p.y());
+    MockMotionEvent mi(MotionEvent::Action::MOVE, t0 + dt * i, p.x(), p.y());
     state_unrestricted.AddMovement(mi);
     state_restricted.AddMovement(mi);
   }
diff --git a/ui/events/gestures/motion_event_aura.cc b/ui/events/gestures/motion_event_aura.cc
index 915faba3..12acaec 100644
--- a/ui/events/gestures/motion_event_aura.cc
+++ b/ui/events/gestures/motion_event_aura.cc
@@ -14,18 +14,18 @@
     EventPointerType type) {
   switch (type) {
     case EventPointerType::POINTER_TYPE_UNKNOWN:
-      return MotionEvent::TOOL_TYPE_UNKNOWN;
+      return MotionEvent::ToolType::UNKNOWN;
     case EventPointerType::POINTER_TYPE_MOUSE:
-      return MotionEvent::TOOL_TYPE_MOUSE;
+      return MotionEvent::ToolType::MOUSE;
     case EventPointerType::POINTER_TYPE_PEN:
-      return MotionEvent::TOOL_TYPE_STYLUS;
+      return MotionEvent::ToolType::STYLUS;
     case EventPointerType::POINTER_TYPE_TOUCH:
-      return MotionEvent::TOOL_TYPE_FINGER;
+      return MotionEvent::ToolType::FINGER;
     case EventPointerType::POINTER_TYPE_ERASER:
-      return MotionEvent::TOOL_TYPE_ERASER;
+      return MotionEvent::ToolType::ERASER;
   }
 
-  return MotionEvent::TOOL_TYPE_UNKNOWN;
+  return MotionEvent::ToolType::UNKNOWN;
 }
 
 PointerProperties GetPointerPropertiesFromTouchEvent(const TouchEvent& touch) {
@@ -128,7 +128,7 @@
   DCHECK(GetPointerCount());
   int index_to_delete = GetIndexFromId(event.pointer_details().id);
   set_action_index(-1);
-  set_action(MotionEvent::ACTION_NONE);
+  set_action(MotionEvent::Action::NONE);
   pointer(index_to_delete) = pointer(GetPointerCount() - 1);
   PopPointer();
 }
@@ -156,25 +156,25 @@
   switch (touch.type()) {
     case ET_TOUCH_PRESSED:
       if (GetPointerCount() == 1) {
-        set_action(ACTION_DOWN);
+        set_action(Action::DOWN);
       } else {
-        set_action(ACTION_POINTER_DOWN);
+        set_action(Action::POINTER_DOWN);
         set_action_index(GetIndexFromId(touch.pointer_details().id));
       }
       break;
     case ET_TOUCH_RELEASED:
       if (GetPointerCount() == 1) {
-        set_action(ACTION_UP);
+        set_action(Action::UP);
       } else {
-        set_action(ACTION_POINTER_UP);
+        set_action(Action::POINTER_UP);
         set_action_index(GetIndexFromId(touch.pointer_details().id));
       }
       break;
     case ET_TOUCH_CANCELLED:
-      set_action(ACTION_CANCEL);
+      set_action(Action::CANCEL);
       break;
     case ET_TOUCH_MOVED:
-      set_action(ACTION_MOVE);
+      set_action(Action::MOVE);
       break;
     default:
       NOTREACHED();
diff --git a/ui/events/gestures/motion_event_aura_unittest.cc b/ui/events/gestures/motion_event_aura_unittest.cc
index 534013d..276876f20 100644
--- a/ui/events/gestures/motion_event_aura_unittest.cc
+++ b/ui/events/gestures/motion_event_aura_unittest.cc
@@ -257,7 +257,7 @@
   EXPECT_FLOAT_EQ(radius_y, event.GetTouchMinor(0) / 2);
   EXPECT_FLOAT_EQ(rotation_angle, gfx::RadToDeg(event.GetOrientation(0)) + 90);
   EXPECT_FLOAT_EQ(pressure, event.GetPressure(0));
-  EXPECT_EQ(MotionEvent::TOOL_TYPE_FINGER, event.GetToolType(0));
+  EXPECT_EQ(MotionEvent::ToolType::FINGER, event.GetToolType(0));
 
   // Test case: radius_x < radius_y, rotation_angle < 90
   radius_x = 67.89f;
@@ -273,7 +273,7 @@
   EXPECT_FLOAT_EQ(radius_x, event.GetTouchMinor(1) / 2);
   EXPECT_FLOAT_EQ(rotation_angle, gfx::RadToDeg(event.GetOrientation(1)));
   EXPECT_FLOAT_EQ(pressure, event.GetPressure(1));
-  EXPECT_EQ(MotionEvent::TOOL_TYPE_FINGER, event.GetToolType(1));
+  EXPECT_EQ(MotionEvent::ToolType::FINGER, event.GetToolType(1));
 
   // Test cloning of tap params
   // TODO(mustaq): Make a separate clone test, crbug.com/450655
@@ -285,7 +285,7 @@
   EXPECT_FLOAT_EQ(radius_x, clone->GetTouchMinor(1) / 2);
   EXPECT_FLOAT_EQ(rotation_angle, gfx::RadToDeg(clone->GetOrientation(1)));
   EXPECT_FLOAT_EQ(pressure, clone->GetPressure(1));
-  EXPECT_EQ(MotionEvent::TOOL_TYPE_FINGER, clone->GetToolType(1));
+  EXPECT_EQ(MotionEvent::ToolType::FINGER, clone->GetToolType(1));
 
   // TODO(mustaq): The move test seems out-of-scope here, crbug.com/450655
   radius_x = 76.98f;
@@ -302,7 +302,7 @@
   EXPECT_FLOAT_EQ(radius_x, event.GetTouchMinor(1) / 2);
   EXPECT_FLOAT_EQ(rotation_angle, gfx::RadToDeg(event.GetOrientation(1)));
   EXPECT_FLOAT_EQ(pressure, event.GetPressure(1));
-  EXPECT_EQ(MotionEvent::TOOL_TYPE_FINGER, event.GetToolType(1));
+  EXPECT_EQ(MotionEvent::ToolType::FINGER, event.GetToolType(1));
 
   // Test case: radius_x > radius_y, rotation_angle > 90
   radius_x = 123.45f;
@@ -318,7 +318,7 @@
   EXPECT_FLOAT_EQ(radius_y, event.GetTouchMinor(2) / 2);
   EXPECT_FLOAT_EQ(rotation_angle, gfx::RadToDeg(event.GetOrientation(2)) + 90);
   EXPECT_FLOAT_EQ(pressure, event.GetPressure(2));
-  EXPECT_EQ(MotionEvent::TOOL_TYPE_FINGER, event.GetToolType(2));
+  EXPECT_EQ(MotionEvent::ToolType::FINGER, event.GetToolType(2));
 
   // Test case: radius_x < radius_y, rotation_angle > 90
   radius_x = 67.89f;
@@ -334,7 +334,7 @@
   EXPECT_FLOAT_EQ(radius_x, event.GetTouchMinor(3) / 2);
   EXPECT_FLOAT_EQ(rotation_angle, gfx::RadToDeg(event.GetOrientation(3)) + 180);
   EXPECT_FLOAT_EQ(pressure, event.GetPressure(3));
-  EXPECT_EQ(MotionEvent::TOOL_TYPE_FINGER, event.GetToolType(3));
+  EXPECT_EQ(MotionEvent::ToolType::FINGER, event.GetToolType(3));
 }
 
 TEST(MotionEventAuraTest, Timestamps) {
@@ -371,36 +371,36 @@
 
   TouchEvent press0 = TouchWithType(ET_TOUCH_PRESSED, ids[0]);
   EXPECT_TRUE(event.OnTouch(press0));
-  EXPECT_EQ(MotionEvent::ACTION_DOWN, event.GetAction());
+  EXPECT_EQ(MotionEvent::Action::DOWN, event.GetAction());
   EXPECT_EQ(1U, event.GetPointerCount());
 
   TouchEvent press1 = TouchWithType(ET_TOUCH_PRESSED, ids[1]);
   EXPECT_TRUE(event.OnTouch(press1));
-  EXPECT_EQ(MotionEvent::ACTION_POINTER_DOWN, event.GetAction());
+  EXPECT_EQ(MotionEvent::Action::POINTER_DOWN, event.GetAction());
   EXPECT_EQ(1, event.GetActionIndex());
   EXPECT_EQ(2U, event.GetPointerCount());
 
   // Test cloning of CachedAction information.
   std::unique_ptr<MotionEvent> clone = event.Clone();
-  EXPECT_EQ(MotionEvent::ACTION_POINTER_DOWN, clone->GetAction());
+  EXPECT_EQ(MotionEvent::Action::POINTER_DOWN, clone->GetAction());
   EXPECT_EQ(1, clone->GetActionIndex());
 
   TouchEvent move0 = TouchWithType(ET_TOUCH_MOVED, ids[0]);
   move0.set_location(gfx::Point(10, 12));
   EXPECT_TRUE(event.OnTouch(move0));
-  EXPECT_EQ(MotionEvent::ACTION_MOVE, event.GetAction());
+  EXPECT_EQ(MotionEvent::Action::MOVE, event.GetAction());
   EXPECT_EQ(2U, event.GetPointerCount());
 
   TouchEvent release0 = TouchWithType(ET_TOUCH_RELEASED, ids[0]);
   EXPECT_TRUE(event.OnTouch(release0));
-  EXPECT_EQ(MotionEvent::ACTION_POINTER_UP, event.GetAction());
+  EXPECT_EQ(MotionEvent::Action::POINTER_UP, event.GetAction());
   EXPECT_EQ(2U, event.GetPointerCount());
   event.CleanupRemovedTouchPoints(release0);
   EXPECT_EQ(1U, event.GetPointerCount());
 
   TouchEvent release1 = TouchWithType(ET_TOUCH_RELEASED, ids[1]);
   EXPECT_TRUE(event.OnTouch(release1));
-  EXPECT_EQ(MotionEvent::ACTION_UP, event.GetAction());
+  EXPECT_EQ(MotionEvent::Action::UP, event.GetAction());
   EXPECT_EQ(1U, event.GetPointerCount());
   event.CleanupRemovedTouchPoints(release1);
   EXPECT_EQ(0U, event.GetPointerCount());
@@ -412,17 +412,17 @@
 
   TouchEvent press0 = TouchWithType(ET_TOUCH_PRESSED, ids[0]);
   EXPECT_TRUE(event.OnTouch(press0));
-  EXPECT_EQ(MotionEvent::ACTION_DOWN, event.GetAction());
+  EXPECT_EQ(MotionEvent::Action::DOWN, event.GetAction());
   EXPECT_EQ(1U, event.GetPointerCount());
 
   TouchEvent press1 = TouchWithType(ET_TOUCH_PRESSED, ids[1]);
   EXPECT_TRUE(event.OnTouch(press1));
-  EXPECT_EQ(MotionEvent::ACTION_POINTER_DOWN, event.GetAction());
+  EXPECT_EQ(MotionEvent::Action::POINTER_DOWN, event.GetAction());
   EXPECT_EQ(1, event.GetActionIndex());
   EXPECT_EQ(2U, event.GetPointerCount());
 
   std::unique_ptr<MotionEvent> cancel = event.Cancel();
-  EXPECT_EQ(MotionEvent::ACTION_CANCEL, cancel->GetAction());
+  EXPECT_EQ(MotionEvent::Action::CANCEL, cancel->GetAction());
   EXPECT_EQ(2U, cancel->GetPointerCount());
 }
 
@@ -431,7 +431,7 @@
   TouchEvent touch_event = TouchWithType(ET_TOUCH_PRESSED, 7);
   EXPECT_TRUE(event.OnTouch(touch_event));
   ASSERT_EQ(1U, event.GetPointerCount());
-  EXPECT_EQ(MotionEvent::TOOL_TYPE_FINGER, event.GetToolType(0));
+  EXPECT_EQ(MotionEvent::ToolType::FINGER, event.GetToolType(0));
 
   touch_event = TouchWithType(ET_TOUCH_RELEASED, 7);
   PointerDetails pointer_details(EventPointerType::POINTER_TYPE_PEN,
@@ -441,7 +441,7 @@
                                  /* force */ 1.0f);
   touch_event.SetPointerDetailsForTest(pointer_details);
   EXPECT_TRUE(event.OnTouch(touch_event));
-  EXPECT_EQ(MotionEvent::TOOL_TYPE_STYLUS, event.GetToolType(0));
+  EXPECT_EQ(MotionEvent::ToolType::STYLUS, event.GetToolType(0));
 }
 
 TEST(MotionEventAuraTest, Flags) {
@@ -501,13 +501,13 @@
 
   TouchEvent press0 = TouchWithType(ET_TOUCH_PRESSED, 3);
   EXPECT_TRUE(event.OnTouch(press0));
-  EXPECT_EQ(MotionEvent::ACTION_DOWN, event.GetAction());
+  EXPECT_EQ(MotionEvent::Action::DOWN, event.GetAction());
   ASSERT_EQ(1U, event.GetPointerCount());
   EXPECT_EQ(event.GetUniqueEventId(), press0.unique_event_id());
 
   TouchEvent press1 = TouchWithType(ET_TOUCH_PRESSED, 6);
   EXPECT_TRUE(event.OnTouch(press1));
-  EXPECT_EQ(MotionEvent::ACTION_POINTER_DOWN, event.GetAction());
+  EXPECT_EQ(MotionEvent::Action::POINTER_DOWN, event.GetAction());
   EXPECT_EQ(2U, event.GetPointerCount());
   EXPECT_EQ(event.GetUniqueEventId(), press1.unique_event_id());
 }
@@ -543,7 +543,7 @@
       ET_TOUCH_PRESSED, gfx::Point(0, 0), base::TimeTicks(),
       ui::PointerDetails(ui::EventPointerType::POINTER_TYPE_PEN, 7));
   EXPECT_TRUE(event.OnTouch(touch_event));
-  EXPECT_EQ(MotionEvent::TOOL_TYPE_STYLUS, event.GetToolType(0));
+  EXPECT_EQ(MotionEvent::ToolType::STYLUS, event.GetToolType(0));
   EXPECT_EQ(1u, event.GetTouchMajor(0));
   EXPECT_EQ(1u, event.GetTouchMinor(0));
 }
diff --git a/ui/events/test/motion_event_test_utils.cc b/ui/events/test/motion_event_test_utils.cc
index 67564146..de123aa2 100644
--- a/ui/events/test/motion_event_test_utils.cc
+++ b/ui/events/test/motion_event_test_utils.cc
@@ -32,8 +32,7 @@
 }  // namespace
 
 MockMotionEvent::MockMotionEvent()
-    : MotionEventGeneric(ACTION_CANCEL, base::TimeTicks(), CreatePointer()) {
-}
+    : MotionEventGeneric(Action::CANCEL, base::TimeTicks(), CreatePointer()) {}
 
 MockMotionEvent::MockMotionEvent(Action action)
     : MotionEventGeneric(action, base::TimeTicks(), CreatePointer()) {
@@ -54,7 +53,7 @@
                                  float y1)
     : MotionEventGeneric(action, time, CreatePointer(x0, y0, 0)) {
   PushPointer(x1, y1);
-  if (action == ACTION_POINTER_UP || action == ACTION_POINTER_DOWN)
+  if (action == Action::POINTER_UP || action == Action::POINTER_DOWN)
     set_action_index(1);
 }
 
@@ -69,7 +68,7 @@
     : MotionEventGeneric(action, time, CreatePointer(x0, y0, 0)) {
   PushPointer(x1, y1);
   PushPointer(x2, y2);
-  if (action == ACTION_POINTER_UP || action == ACTION_POINTER_DOWN)
+  if (action == Action::POINTER_UP || action == Action::POINTER_DOWN)
     set_action_index(2);
 }
 
@@ -79,7 +78,7 @@
   set_action(action);
   set_event_time(time);
   set_unique_event_id(ui::GetNextTouchEventId());
-  if (action == ACTION_POINTER_UP || action == ACTION_POINTER_DOWN)
+  if (action == Action::POINTER_UP || action == Action::POINTER_DOWN)
     set_action_index(static_cast<int>(positions.size()) - 1);
   for (size_t i = 0; i < positions.size(); ++i)
     PushPointer(positions[i].x(), positions[i].y());
@@ -97,9 +96,9 @@
   PushPointer(x, y);
   if (GetPointerCount() > 1) {
     set_action_index(static_cast<int>(GetPointerCount()) - 1);
-    set_action(ACTION_POINTER_DOWN);
+    set_action(Action::POINTER_DOWN);
   } else {
-    set_action(ACTION_DOWN);
+    set_action(Action::DOWN);
   }
   return *this;
 }
@@ -114,7 +113,7 @@
   p.y = y;
   p.raw_x += dx;
   p.raw_y += dy;
-  set_action(ACTION_MOVE);
+  set_action(Action::MOVE);
   return *this;
 }
 
@@ -123,9 +122,9 @@
   switch (GetAction()) {
     // If the previous action is one of those who need removing a pointer in
     // UpdatePointersAndID, then the last index will be GetPointerCount() - 2.
-    case ACTION_POINTER_UP:
-    case ACTION_UP:
-    case ACTION_CANCEL:
+    case Action::POINTER_UP:
+    case Action::UP:
+    case Action::CANCEL:
       return ReleasePointAtIndex(GetPointerCount() - 2);
       break;
     default:
@@ -139,9 +138,9 @@
   DCHECK_LT(index, GetPointerCount());
   if (GetPointerCount() > 1) {
     set_action_index(static_cast<int>(index));
-    set_action(ACTION_POINTER_UP);
+    set_action(Action::POINTER_UP);
   } else {
-    set_action(ACTION_UP);
+    set_action(Action::UP);
   }
   return *this;
 }
@@ -149,7 +148,7 @@
 MockMotionEvent& MockMotionEvent::CancelPoint() {
   UpdatePointersAndID();
   DCHECK_GT(GetPointerCount(), 0U);
-  set_action(ACTION_CANCEL);
+  set_action(Action::CANCEL);
   return *this;
 }
 
@@ -183,14 +182,14 @@
 void MockMotionEvent::UpdatePointersAndID() {
   set_unique_event_id(ui::GetNextTouchEventId());
   switch (GetAction()) {
-    case ACTION_POINTER_UP: {
+    case Action::POINTER_UP: {
       int index = GetActionIndex();
       DCHECK_LT(index, static_cast<int>(GetPointerCount()));
       RemovePointerAt(index);
       break;
     }
-    case ACTION_UP:
-    case ACTION_CANCEL:
+    case Action::UP:
+    case Action::CANCEL:
       PopPointer();
       break;
     default:
@@ -209,8 +208,8 @@
   std::stringstream ss;
   ss << "MotionEvent {"
      << "\n Action: " << event.GetAction();
-  if (event.GetAction() == MotionEvent::ACTION_POINTER_DOWN ||
-      event.GetAction() == MotionEvent::ACTION_POINTER_UP)
+  if (event.GetAction() == MotionEvent::Action::POINTER_DOWN ||
+      event.GetAction() == MotionEvent::Action::POINTER_UP)
     ss << "\n ActionIndex: " << event.GetActionIndex();
   ss << "\n Flags: " << event.GetFlags()
      << "\n ButtonState: " << event.GetButtonState() << "\n Pointers: [";
diff --git a/ui/message_center/public/cpp/notification_delegate.cc b/ui/message_center/public/cpp/notification_delegate.cc
index a39ec92..42b536b9 100644
--- a/ui/message_center/public/cpp/notification_delegate.cc
+++ b/ui/message_center/public/cpp/notification_delegate.cc
@@ -9,22 +9,45 @@
 
 namespace message_center {
 
-// NotificationDelegate:
+// ThunkNotificationDelegate:
 
-void NotificationDelegate::Close(bool by_user) {}
+ThunkNotificationDelegate::ThunkNotificationDelegate(
+    base::WeakPtr<NotificationObserver> impl)
+    : impl_(impl) {}
 
-void NotificationDelegate::Click() {}
-
-void NotificationDelegate::ButtonClick(int button_index) {}
-
-void NotificationDelegate::ButtonClickWithReply(int button_index,
-                                                const base::string16& reply) {
-  NOTIMPLEMENTED();
+void ThunkNotificationDelegate::Close(bool by_user) {
+  if (impl_)
+    impl_->Close(by_user);
 }
 
-void NotificationDelegate::SettingsClick() {}
+void ThunkNotificationDelegate::Click() {
+  if (impl_)
+    impl_->Click();
+}
 
-void NotificationDelegate::DisableNotification() {}
+void ThunkNotificationDelegate::ButtonClick(int button_index) {
+  if (impl_)
+    impl_->ButtonClick(button_index);
+}
+
+void ThunkNotificationDelegate::ButtonClickWithReply(
+    int button_index,
+    const base::string16& reply) {
+  if (impl_)
+    impl_->ButtonClickWithReply(button_index, reply);
+}
+
+void ThunkNotificationDelegate::SettingsClick() {
+  if (impl_)
+    impl_->SettingsClick();
+}
+
+void ThunkNotificationDelegate::DisableNotification() {
+  if (impl_)
+    impl_->DisableNotification();
+}
+
+ThunkNotificationDelegate::~ThunkNotificationDelegate() = default;
 
 // HandleNotificationClickDelegate:
 
diff --git a/ui/message_center/public/cpp/notification_delegate.h b/ui/message_center/public/cpp/notification_delegate.h
index 87dc053..8f5a532 100644
--- a/ui/message_center/public/cpp/notification_delegate.h
+++ b/ui/message_center/public/cpp/notification_delegate.h
@@ -11,46 +11,78 @@
 #include "base/callback.h"
 #include "base/macros.h"
 #include "base/memory/ref_counted.h"
+#include "base/memory/weak_ptr.h"
 #include "base/optional.h"
 #include "base/strings/string16.h"
 #include "ui/message_center/public/cpp/message_center_public_export.h"
 
 namespace message_center {
 
-// Delegate for a notification. This class has two roles: to implement callback
-// methods for notification, and to provide an identity of the associated
-// notification.
-class MESSAGE_CENTER_PUBLIC_EXPORT NotificationDelegate
-    : public base::RefCountedThreadSafe<NotificationDelegate> {
+// Handles actions performed on a notification.
+class MESSAGE_CENTER_PUBLIC_EXPORT NotificationObserver {
  public:
   // To be called when the desktop notification is closed.  If closed by a
   // user explicitly (as opposed to timeout/script), |by_user| should be true.
-  virtual void Close(bool by_user);
+  virtual void Close(bool by_user) {}
 
   // To be called when a desktop notification is clicked.
-  virtual void Click();
+  virtual void Click() {}
 
   // To be called when the user clicks a button in a notification.
-  virtual void ButtonClick(int button_index);
+  virtual void ButtonClick(int button_index) {}
 
   // To be called when the user types a reply to a notification.
   virtual void ButtonClickWithReply(int button_index,
-                                    const base::string16& reply);
+                                    const base::string16& reply) {}
 
   // To be called when the user clicks the settings button in a notification
   // which has a DELEGATE settings button action.
-  virtual void SettingsClick();
+  virtual void SettingsClick() {}
 
   // Called when the user attempts to disable the notification.
-  virtual void DisableNotification();
+  virtual void DisableNotification() {}
+};
 
+// Ref counted version of NotificationObserver, required to satisfy
+// message_center::Notification::delegate_.
+class MESSAGE_CENTER_PUBLIC_EXPORT NotificationDelegate
+    : public NotificationObserver,
+      public base::RefCountedThreadSafe<NotificationDelegate> {
  protected:
-  virtual ~NotificationDelegate() {}
+  virtual ~NotificationDelegate() = default;
 
  private:
   friend class base::RefCountedThreadSafe<NotificationDelegate>;
 };
 
+// A pass-through which converts the RefCounted requirement to a WeakPtr
+// requirement. This class replaces the need for individual delegates that pass
+// through to an actual controller class, and which only exist because the
+// actual controller has a strong ownership model. TODO(estade): replace many
+// existing NotificationDelegate overrides with an instance of this class.
+class MESSAGE_CENTER_PUBLIC_EXPORT ThunkNotificationDelegate
+    : public NotificationDelegate {
+ public:
+  explicit ThunkNotificationDelegate(base::WeakPtr<NotificationObserver> impl);
+
+  // NotificationDelegate:
+  void Close(bool by_user) override;
+  void Click() override;
+  void ButtonClick(int button_index) override;
+  void ButtonClickWithReply(int button_index,
+                            const base::string16& reply) override;
+  void SettingsClick() override;
+  void DisableNotification() override;
+
+ protected:
+  ~ThunkNotificationDelegate() override;
+
+ private:
+  base::WeakPtr<NotificationObserver> impl_;
+
+  DISALLOW_COPY_AND_ASSIGN(ThunkNotificationDelegate);
+};
+
 // A simple notification delegate which invokes the passed closure when the body
 // or a button is clicked.
 class MESSAGE_CENTER_PUBLIC_EXPORT HandleNotificationClickDelegate
diff --git a/ui/ozone/platform/drm/DEPS b/ui/ozone/platform/drm/DEPS
index 7d49ef1..69f499a 100644
--- a/ui/ozone/platform/drm/DEPS
+++ b/ui/ozone/platform/drm/DEPS
@@ -2,6 +2,7 @@
   "+mojo/public",
   "+services/service_manager",
   "+services/ui",
+  "+ui/base/ui_base_features.h",
   "+ui/base/ui_base_switches.h",
   "+ui/base/ui_features.h",  # UI features doesn't bring in all of ui/base.
   "+ui/display/util",
diff --git a/ui/ozone/platform/drm/gpu/drm_thread.h b/ui/ozone/platform/drm/gpu/drm_thread.h
index f9dd17a..1faf9994 100644
--- a/ui/ozone/platform/drm/gpu/drm_thread.h
+++ b/ui/ozone/platform/drm/gpu/drm_thread.h
@@ -158,7 +158,7 @@
 
   // The mojo implementation of DrmDevice requires a BindingSet because the
   // DrmThread services requests from different client threads when operating in
-  // --mus mode
+  // mus mode
   mojo::BindingSet<ozone::mojom::DrmDevice> drm_bindings_;
 
   base::WeakPtrFactory<DrmThread> weak_ptr_factory_;
diff --git a/ui/ozone/platform/drm/host/drm_device_connector.cc b/ui/ozone/platform/drm/host/drm_device_connector.cc
index 547e156..6bfc3cb 100644
--- a/ui/ozone/platform/drm/host/drm_device_connector.cc
+++ b/ui/ozone/platform/drm/host/drm_device_connector.cc
@@ -4,12 +4,11 @@
 
 #include "ui/ozone/platform/drm/host/drm_device_connector.h"
 
-#include "base/command_line.h"
 #include "mojo/public/cpp/bindings/interface_request.h"
 #include "mojo/public/cpp/system/message_pipe.h"
 #include "services/service_manager/public/cpp/connector.h"
 #include "services/ui/public/interfaces/constants.mojom.h"
-#include "ui/base/ui_base_switches.h"
+#include "ui/base/ui_base_features.h"
 #include "ui/ozone/platform/drm/host/host_drm_device.h"
 #include "ui/ozone/public/gpu_platform_support_host.h"
 
@@ -43,11 +42,9 @@
       ws_runner_(base::ThreadTaskRunnerHandle::IsSet()
                      ? base::ThreadTaskRunnerHandle::Get()
                      : nullptr) {
-  // Invariant: we only have a runner at startup if executing in --mus mode.
-  DCHECK((ws_runner_ &&
-          base::CommandLine::ForCurrentProcess()->HasSwitch(switches::kMus)) ||
-         (!ws_runner_ &&
-          !base::CommandLine::ForCurrentProcess()->HasSwitch(switches::kMus)));
+  // Invariant: we only have a runner at startup if executing in mus mode.
+  DCHECK((ws_runner_ && features::IsMusEnabled()) ||
+         (!ws_runner_ && !features::IsMusEnabled()));
 }
 
 DrmDeviceConnector::~DrmDeviceConnector() {}
@@ -137,4 +134,4 @@
   }
 }
 
-}  // namespace ui
\ No newline at end of file
+}  // namespace ui
diff --git a/ui/ozone/platform/drm/host/drm_gpu_platform_support_host.cc b/ui/ozone/platform/drm/host/drm_gpu_platform_support_host.cc
index df8585d..6c7f4f4b 100644
--- a/ui/ozone/platform/drm/host/drm_gpu_platform_support_host.cc
+++ b/ui/ozone/platform/drm/host/drm_gpu_platform_support_host.cc
@@ -9,6 +9,7 @@
 #include "base/command_line.h"
 #include "base/threading/thread_task_runner_handle.h"
 #include "base/trace_event/trace_event.h"
+#include "ui/base/ui_base_features.h"
 #include "ui/base/ui_base_switches.h"
 #include "ui/ozone/common/gpu/ozone_gpu_message_params.h"
 #include "ui/ozone/common/gpu/ozone_gpu_messages.h"
@@ -94,7 +95,7 @@
   if (ui_runner_) {
     weak_ptr_ = weak_ptr_factory_.GetWeakPtr();
   } else {
-    DCHECK(!base::CommandLine::ForCurrentProcess()->HasSwitch(switches::kMus));
+    DCHECK(!features::IsMusEnabled());
   }
 }
 
diff --git a/ui/ozone/platform/drm/host/drm_overlay_manager.h b/ui/ozone/platform/drm/host/drm_overlay_manager.h
index 03b5e661..1dd9aa15 100644
--- a/ui/ozone/platform/drm/host/drm_overlay_manager.h
+++ b/ui/ozone/platform/drm/host/drm_overlay_manager.h
@@ -71,7 +71,7 @@
   base::MRUCache<OverlaySurfaceCandidateList, OverlayValidationCacheValue>
       cache_;
   // The cache can be accessed from multiple threads in some cases (e.g. with
-  // --mus, it can be accessed from the UI thread, and the window-service
+  // mus, it can be accessed from the UI thread, and the window-service
   // thread.)
   // TODO(rjkroege): In the future (with --enable-viz), this code will not need
   // the lock, but will require farther refactoring.
diff --git a/ui/ozone/platform/drm/host/host_drm_device.h b/ui/ozone/platform/drm/host/host_drm_device.h
index 65f6c8b..0a5b41c8 100644
--- a/ui/ozone/platform/drm/host/host_drm_device.h
+++ b/ui/ozone/platform/drm/host/host_drm_device.h
@@ -140,7 +140,7 @@
   // Mojo implementation of the DrmDevice. Will be bound on the "main" thread.
   ui::ozone::mojom::DrmDevicePtr drm_device_ptr_;
 
-  // When running under --mus, this is the UI thread specific DrmDevice ptr for
+  // When running under mus, this is the UI thread specific DrmDevice ptr for
   // use by the compositor.
   ui::ozone::mojom::DrmDevicePtr drm_device_ptr_compositor_;
 
@@ -153,7 +153,7 @@
   THREAD_CHECKER(on_io_thread_);  // Needs to be rebound as is allocated on the
                                   // window server  thread.
   THREAD_CHECKER(on_window_server_thread_);
-  // When running under --mus, some entry points are used from the mus thread
+  // When running under mus, some entry points are used from the mus thread
   // and some are used from the ui thread. In general. In that case, the
   // on_ui_thread_ and on_window_server_thread_ will differ. In particular,
   // entry points used by the compositor use the ui thread.
diff --git a/ui/ozone/public/client_native_pixmap_factory_ozone.cc b/ui/ozone/public/client_native_pixmap_factory_ozone.cc
index 32b6968..110f816 100644
--- a/ui/ozone/public/client_native_pixmap_factory_ozone.cc
+++ b/ui/ozone/public/client_native_pixmap_factory_ozone.cc
@@ -17,8 +17,8 @@
 
 // Thread-safe owner of the gfx::ClientNativePixmapFactory. Not a LazyInstance
 // because it uses PlatformObject<>::Create() for factory construction.
-// TODO(jamescook|spang): This exists to solve a startup race for chrome --mash
-// http://crbug.com/807781. Removing the factory entirely would be better,
+// TODO(jamescook|spang): This exists to solve a startup race for chrome with
+// mash http://crbug.com/807781. Removing the factory entirely would be better,
 // with something like http://crrev.com/c/899949.
 class PixmapFactorySingleton {
  public:
diff --git a/ui/resources/ui_resources.grd b/ui/resources/ui_resources.grd
index 708caaf..10be1c5a 100644
--- a/ui/resources/ui_resources.grd
+++ b/ui/resources/ui_resources.grd
@@ -118,7 +118,7 @@
         <structure type="chrome_scaled_image" name="IDR_MENU_HIERARCHY_ARROW" file="mac/menu_hierarchy_arrow.png" />
       </if>
       <structure type="chrome_scaled_image" name="IDR_MENU_DROPARROW" file="cros/menu_droparrow.png" />
-      <if expr="toolkit_views or is_macosx or is_ios">
+      <if expr="toolkit_views or is_ios">
         <if expr="is_win">
           <structure type="chrome_scaled_image" name="IDR_NOTIFICATION_CLOSE" file="win/notification_close.png"/>
           <structure type="chrome_scaled_image" name="IDR_NOTIFICATION_CLOSE_HOVER" file="win/notification_close_hover.png"/>
diff --git a/ui/touch_selection/longpress_drag_selector.cc b/ui/touch_selection/longpress_drag_selector.cc
index fdc3221..df96825 100644
--- a/ui/touch_selection/longpress_drag_selector.cc
+++ b/ui/touch_selection/longpress_drag_selector.cc
@@ -28,19 +28,19 @@
 
 bool LongPressDragSelector::WillHandleTouchEvent(const MotionEvent& event) {
   switch (event.GetAction()) {
-    case MotionEvent::ACTION_DOWN:
+    case MotionEvent::Action::DOWN:
       touch_down_position_.SetPoint(event.GetX(), event.GetY());
       touch_down_time_ = event.GetEventTime();
       has_longpress_drag_start_anchor_ = false;
       SetState(LONGPRESS_PENDING);
       return false;
 
-    case MotionEvent::ACTION_UP:
-    case MotionEvent::ACTION_CANCEL:
+    case MotionEvent::Action::UP:
+    case MotionEvent::Action::CANCEL:
       SetState(INACTIVE);
       return false;
 
-    case MotionEvent::ACTION_MOVE:
+    case MotionEvent::Action::MOVE:
       break;
 
     default:
diff --git a/ui/touch_selection/touch_handle.cc b/ui/touch_selection/touch_handle.cc
index ee1c765..65b1dba 100644
--- a/ui/touch_selection/touch_handle.cc
+++ b/ui/touch_selection/touch_handle.cc
@@ -165,11 +165,11 @@
   if (!enabled_)
     return false;
 
-  if (!is_dragging_ && event.GetAction() != MotionEvent::ACTION_DOWN)
+  if (!is_dragging_ && event.GetAction() != MotionEvent::Action::DOWN)
     return false;
 
   switch (event.GetAction()) {
-    case MotionEvent::ACTION_DOWN: {
+    case MotionEvent::Action::DOWN: {
       if (!is_visible_)
         return false;
       const gfx::PointF touch_point(event.GetX(), event.GetY());
@@ -191,7 +191,7 @@
       BeginDrag();
     } break;
 
-    case MotionEvent::ACTION_MOVE: {
+    case MotionEvent::Action::MOVE: {
       gfx::PointF touch_move_position(event.GetX(), event.GetY());
       is_drag_within_tap_region_ &=
           client_->IsWithinTapSlop(touch_down_position_ - touch_move_position);
@@ -201,7 +201,7 @@
       client_->OnDragUpdate(*this, touch_move_position + touch_drag_offset_);
     } break;
 
-    case MotionEvent::ACTION_UP: {
+    case MotionEvent::Action::UP: {
       if (is_drag_within_tap_region_ &&
           (event.GetEventTime() - touch_down_time_) <
               client_->GetMaxTapDuration()) {
@@ -211,7 +211,7 @@
       EndDrag();
     } break;
 
-    case MotionEvent::ACTION_CANCEL:
+    case MotionEvent::Action::CANCEL:
       EndDrag();
       break;
 
diff --git a/ui/touch_selection/touch_handle_unittest.cc b/ui/touch_selection/touch_handle_unittest.cc
index 84c9db2..c723009 100644
--- a/ui/touch_selection/touch_handle_unittest.cc
+++ b/ui/touch_selection/touch_handle_unittest.cc
@@ -331,8 +331,8 @@
   // Dragging should not be allowed while the handle is disabled.
   base::TimeTicks event_time = base::TimeTicks::Now();
   const float kOffset = kDefaultDrawableSize / 2.f;
-  MockMotionEvent event(
-      MockMotionEvent::ACTION_DOWN, event_time, kOffset, kOffset);
+  MockMotionEvent event(MockMotionEvent::Action::DOWN, event_time, kOffset,
+                        kOffset);
   EXPECT_FALSE(handle.WillHandleTouchEvent(event));
 
   // Disabling mid-animation should cancel the animation.
@@ -366,50 +366,51 @@
   const float kOffset = kDefaultDrawableSize / 2.f;
 
   // The handle must be visible to trigger drag.
-  MockMotionEvent event(
-      MockMotionEvent::ACTION_DOWN, event_time, kOffset, kOffset);
+  MockMotionEvent event(MockMotionEvent::Action::DOWN, event_time, kOffset,
+                        kOffset);
   EXPECT_FALSE(handle.WillHandleTouchEvent(event));
   EXPECT_FALSE(IsDragging());
   UpdateHandleVisibility(handle, true, TouchHandle::ANIMATION_NONE);
 
-  // ACTION_DOWN must fall within the drawable region to trigger drag.
-  event = MockMotionEvent(MockMotionEvent::ACTION_DOWN, event_time, 50, 50);
+  // Action::DOWN must fall within the drawable region to trigger drag.
+  event = MockMotionEvent(MockMotionEvent::Action::DOWN, event_time, 50, 50);
   EXPECT_FALSE(handle.WillHandleTouchEvent(event));
   EXPECT_FALSE(IsDragging());
 
-  // Only ACTION_DOWN will trigger drag.
-  event = MockMotionEvent(
-      MockMotionEvent::ACTION_MOVE, event_time, kOffset, kOffset);
+  // Only Action::DOWN will trigger drag.
+  event = MockMotionEvent(MockMotionEvent::Action::MOVE, event_time, kOffset,
+                          kOffset);
   EXPECT_FALSE(handle.WillHandleTouchEvent(event));
   EXPECT_FALSE(IsDragging());
 
   // Start the drag.
-  event = MockMotionEvent(
-      MockMotionEvent::ACTION_DOWN, event_time, kOffset, kOffset);
+  event = MockMotionEvent(MockMotionEvent::Action::DOWN, event_time, kOffset,
+                          kOffset);
   EXPECT_TRUE(handle.WillHandleTouchEvent(event));
   EXPECT_TRUE(IsDragging());
 
-  event = MockMotionEvent(
-      MockMotionEvent::ACTION_MOVE, event_time, kOffset + 10, kOffset + 15);
+  event = MockMotionEvent(MockMotionEvent::Action::MOVE, event_time,
+                          kOffset + 10, kOffset + 15);
   EXPECT_TRUE(handle.WillHandleTouchEvent(event));
   EXPECT_TRUE(GetAndResetHandleDragged());
   EXPECT_TRUE(IsDragging());
   EXPECT_EQ(gfx::PointF(10, 15), DragPosition());
 
-  event = MockMotionEvent(
-      MockMotionEvent::ACTION_MOVE, event_time, kOffset - 10, kOffset - 15);
+  event = MockMotionEvent(MockMotionEvent::Action::MOVE, event_time,
+                          kOffset - 10, kOffset - 15);
   EXPECT_TRUE(handle.WillHandleTouchEvent(event));
   EXPECT_TRUE(GetAndResetHandleDragged());
   EXPECT_TRUE(IsDragging());
   EXPECT_EQ(gfx::PointF(-10, -15), DragPosition());
 
-  event = MockMotionEvent(MockMotionEvent::ACTION_UP);
+  event = MockMotionEvent(MockMotionEvent::Action::UP);
   EXPECT_TRUE(handle.WillHandleTouchEvent(event));
   EXPECT_FALSE(GetAndResetHandleDragged());
   EXPECT_FALSE(IsDragging());
 
-  // Non-ACTION_DOWN events after the drag has terminated should not be handled.
-  event = MockMotionEvent(MockMotionEvent::ACTION_CANCEL);
+  // Non-Action::DOWN events after the drag has terminated should not be
+  // handled.
+  event = MockMotionEvent(MockMotionEvent::Action::CANCEL);
   EXPECT_FALSE(handle.WillHandleTouchEvent(event));
 }
 
@@ -418,7 +419,7 @@
   ASSERT_EQ(drawable().orientation, TouchHandleOrientation::RIGHT);
   UpdateHandleVisibility(handle, true, TouchHandle::ANIMATION_NONE);
 
-  MockMotionEvent event(MockMotionEvent::ACTION_DOWN);
+  MockMotionEvent event(MockMotionEvent::Action::DOWN);
   EXPECT_TRUE(handle.WillHandleTouchEvent(event));
   EXPECT_TRUE(IsDragging());
 
@@ -426,13 +427,13 @@
   UpdateHandleOrientation(handle, TouchHandleOrientation::LEFT);
   EXPECT_EQ(TouchHandleOrientation::RIGHT, drawable().orientation);
 
-  event = MockMotionEvent(MockMotionEvent::ACTION_MOVE);
+  event = MockMotionEvent(MockMotionEvent::Action::MOVE);
   EXPECT_TRUE(handle.WillHandleTouchEvent(event));
   EXPECT_TRUE(GetAndResetHandleDragged());
   EXPECT_TRUE(IsDragging());
   EXPECT_EQ(TouchHandleOrientation::RIGHT, drawable().orientation);
 
-  event = MockMotionEvent(MockMotionEvent::ACTION_UP);
+  event = MockMotionEvent(MockMotionEvent::Action::UP);
   EXPECT_TRUE(handle.WillHandleTouchEvent(event));
   EXPECT_FALSE(GetAndResetHandleDragged());
   EXPECT_FALSE(IsDragging());
@@ -444,7 +445,7 @@
                      kDefaultViewportRect);
   UpdateHandleVisibility(handle, true, TouchHandle::ANIMATION_NONE);
 
-  MockMotionEvent event(MockMotionEvent::ACTION_DOWN);
+  MockMotionEvent event(MockMotionEvent::Action::DOWN);
   EXPECT_TRUE(handle.WillHandleTouchEvent(event));
   EXPECT_TRUE(IsDragging());
 
@@ -454,12 +455,12 @@
   EXPECT_TRUE(drawable().visible);
   EXPECT_EQ(1.f, drawable().alpha);
 
-  event = MockMotionEvent(MockMotionEvent::ACTION_MOVE);
+  event = MockMotionEvent(MockMotionEvent::Action::MOVE);
   EXPECT_TRUE(handle.WillHandleTouchEvent(event));
   EXPECT_FALSE(NeedsAnimate());
   EXPECT_TRUE(drawable().visible);
 
-  event = MockMotionEvent(MockMotionEvent::ACTION_UP);
+  event = MockMotionEvent(MockMotionEvent::Action::UP);
   EXPECT_TRUE(handle.WillHandleTouchEvent(event));
   EXPECT_FALSE(IsDragging());
   EXPECT_TRUE(NeedsAnimate());
@@ -481,8 +482,7 @@
   const float kTouchSize = 24.f;
   const float kOffset = kDefaultDrawableSize + kTouchSize / 2.001f;
 
-  MockMotionEvent event(
-      MockMotionEvent::ACTION_DOWN, event_time, kOffset, 0);
+  MockMotionEvent event(MockMotionEvent::Action::DOWN, event_time, kOffset, 0);
   event.SetTouchMajor(0.f);
   EXPECT_FALSE(handle.WillHandleTouchEvent(event));
   EXPECT_FALSE(IsDragging());
@@ -500,8 +500,8 @@
   EXPECT_TRUE(IsDragging());
 
   // The touch hit test region should be circular.
-  event = MockMotionEvent(
-      MockMotionEvent::ACTION_DOWN, event_time, kOffset, kOffset);
+  event = MockMotionEvent(MockMotionEvent::Action::DOWN, event_time, kOffset,
+                          kOffset);
   event.SetTouchMajor(kTouchSize);
   EXPECT_FALSE(handle.WillHandleTouchEvent(event));
   EXPECT_FALSE(IsDragging());
@@ -515,17 +515,16 @@
   EXPECT_TRUE(IsDragging());
 
   // Ensure a touch size of 0 can still register a hit.
-  event = MockMotionEvent(MockMotionEvent::ACTION_DOWN,
-                          event_time,
-                          kDefaultDrawableSize / 2.f,
-                          kDefaultDrawableSize / 2.f);
+  event =
+      MockMotionEvent(MockMotionEvent::Action::DOWN, event_time,
+                      kDefaultDrawableSize / 2.f, kDefaultDrawableSize / 2.f);
   event.SetTouchMajor(0);
   EXPECT_TRUE(handle.WillHandleTouchEvent(event));
   EXPECT_TRUE(IsDragging());
 
   // Touches centered above the handle region should never register a hit, even
   // if the touch region intersects the handle region.
-  event = MockMotionEvent(MockMotionEvent::ACTION_DOWN, event_time,
+  event = MockMotionEvent(MockMotionEvent::Action::DOWN, event_time,
                           kDefaultDrawableSize / 2.f, -kTouchSize / 3.f);
   event.SetTouchMajor(kTouchSize);
   EXPECT_FALSE(handle.WillHandleTouchEvent(event));
@@ -539,43 +538,43 @@
 
   base::TimeTicks event_time = base::TimeTicks::Now();
 
-  // ACTION_CANCEL shouldn't trigger a tap.
-  MockMotionEvent event(MockMotionEvent::ACTION_DOWN, event_time, 0, 0);
+  // Action::CANCEL shouldn't trigger a tap.
+  MockMotionEvent event(MockMotionEvent::Action::DOWN, event_time, 0, 0);
   EXPECT_TRUE(handle.WillHandleTouchEvent(event));
   event_time += base::TimeDelta::FromMilliseconds(50);
-  event = MockMotionEvent(MockMotionEvent::ACTION_CANCEL, event_time, 0, 0);
+  event = MockMotionEvent(MockMotionEvent::Action::CANCEL, event_time, 0, 0);
   EXPECT_TRUE(handle.WillHandleTouchEvent(event));
   EXPECT_FALSE(GetAndResetHandleTapped());
 
   // Long press shouldn't trigger a tap.
-  event = MockMotionEvent(MockMotionEvent::ACTION_DOWN, event_time, 0, 0);
+  event = MockMotionEvent(MockMotionEvent::Action::DOWN, event_time, 0, 0);
   EXPECT_TRUE(handle.WillHandleTouchEvent(event));
   event_time += 2 * GetMaxTapDuration();
-  event = MockMotionEvent(MockMotionEvent::ACTION_UP, event_time, 0, 0);
+  event = MockMotionEvent(MockMotionEvent::Action::UP, event_time, 0, 0);
   EXPECT_TRUE(handle.WillHandleTouchEvent(event));
   EXPECT_FALSE(GetAndResetHandleTapped());
 
   // Only a brief tap within the slop region should trigger a tap.
-  event = MockMotionEvent(MockMotionEvent::ACTION_DOWN, event_time, 0, 0);
+  event = MockMotionEvent(MockMotionEvent::Action::DOWN, event_time, 0, 0);
   EXPECT_TRUE(handle.WillHandleTouchEvent(event));
   event_time += GetMaxTapDuration() / 2;
-  event = MockMotionEvent(
-      MockMotionEvent::ACTION_MOVE, event_time, kDefaultTapSlop / 2.f, 0);
+  event = MockMotionEvent(MockMotionEvent::Action::MOVE, event_time,
+                          kDefaultTapSlop / 2.f, 0);
   EXPECT_TRUE(handle.WillHandleTouchEvent(event));
-  event = MockMotionEvent(
-      MockMotionEvent::ACTION_UP, event_time, kDefaultTapSlop / 2.f, 0);
+  event = MockMotionEvent(MockMotionEvent::Action::UP, event_time,
+                          kDefaultTapSlop / 2.f, 0);
   EXPECT_TRUE(handle.WillHandleTouchEvent(event));
   EXPECT_TRUE(GetAndResetHandleTapped());
 
   // Moving beyond the slop region shouldn't trigger a tap.
-  event = MockMotionEvent(MockMotionEvent::ACTION_DOWN, event_time, 0, 0);
+  event = MockMotionEvent(MockMotionEvent::Action::DOWN, event_time, 0, 0);
   EXPECT_TRUE(handle.WillHandleTouchEvent(event));
   event_time += GetMaxTapDuration() / 2;
-  event = MockMotionEvent(
-      MockMotionEvent::ACTION_MOVE, event_time, kDefaultTapSlop * 2.f, 0);
+  event = MockMotionEvent(MockMotionEvent::Action::MOVE, event_time,
+                          kDefaultTapSlop * 2.f, 0);
   EXPECT_TRUE(handle.WillHandleTouchEvent(event));
-  event = MockMotionEvent(
-      MockMotionEvent::ACTION_UP, event_time, kDefaultTapSlop * 2.f, 0);
+  event = MockMotionEvent(MockMotionEvent::Action::UP, event_time,
+                          kDefaultTapSlop * 2.f, 0);
   EXPECT_TRUE(handle.WillHandleTouchEvent(event));
   EXPECT_FALSE(GetAndResetHandleTapped());
 }
@@ -639,7 +638,7 @@
   const float kOffset = kDefaultDrawableSize / 2.f;
 
   // Start the drag.
-  MockMotionEvent event(MockMotionEvent::ACTION_DOWN, event_time, kOffset,
+  MockMotionEvent event(MockMotionEvent::Action::DOWN, event_time, kOffset,
                         kOffset);
   EXPECT_TRUE(handle.WillHandleTouchEvent(event));
   EXPECT_TRUE(IsDragging());
@@ -652,7 +651,7 @@
   EXPECT_FALSE(drawable().mirror_horizontal);
 
   // Mirror flag changes will be deferred until the drag ends.
-  event = MockMotionEvent(MockMotionEvent::ACTION_UP);
+  event = MockMotionEvent(MockMotionEvent::Action::UP);
   EXPECT_TRUE(handle.WillHandleTouchEvent(event));
   EXPECT_FALSE(GetAndResetHandleDragged());
   EXPECT_FALSE(IsDragging());
diff --git a/ui/touch_selection/touch_selection_controller.cc b/ui/touch_selection/touch_selection_controller.cc
index 90535dd..8ea5e0b 100644
--- a/ui/touch_selection/touch_selection_controller.cc
+++ b/ui/touch_selection/touch_selection_controller.cc
@@ -172,13 +172,13 @@
 
 bool TouchSelectionController::WillHandleTouchEvent(const MotionEvent& event) {
   bool handled = WillHandleTouchEventImpl(event);
-  // If ACTION_DOWN is consumed, the rest of touch sequence should be consumed,
+  // If Action::DOWN is consumed, the rest of touch sequence should be consumed,
   // too, regardless of value of |handled|.
-  // TODO(mohsen): This will consume touch events until the next ACTION_DOWN.
-  // Ideally we should consume until the final ACTION_UP/ACTION_CANCEL.
-  // But, apparently, we can't reliably determine the final ACTION_CANCEL in a
+  // TODO(mohsen): This will consume touch events until the next Action::DOWN.
+  // Ideally we should consume until the final Action::UP/Action::CANCEL.
+  // But, apparently, we can't reliably determine the final Action::CANCEL in a
   // multi-touch scenario. See https://crbug.com/653212.
-  if (event.GetAction() == MotionEvent::ACTION_DOWN)
+  if (event.GetAction() == MotionEvent::Action::DOWN)
     consume_touch_sequence_ = handled;
   return handled || consume_touch_sequence_;
 }
diff --git a/ui/touch_selection/touch_selection_controller_unittest.cc b/ui/touch_selection/touch_selection_controller_unittest.cc
index a6ddd99..d0bf65b 100644
--- a/ui/touch_selection/touch_selection_controller_unittest.cc
+++ b/ui/touch_selection/touch_selection_controller_unittest.cc
@@ -328,7 +328,7 @@
   OnTapEvent();
 
   // The touch sequence should not be handled if insertion is not active.
-  MockMotionEvent event(MockMotionEvent::ACTION_DOWN, event_time, 0, 0);
+  MockMotionEvent event(MockMotionEvent::Action::DOWN, event_time, 0, 0);
   EXPECT_FALSE(controller().WillHandleTouchEvent(event));
 
   float line_height = 10.f;
@@ -349,30 +349,30 @@
   // The MoveCaret() result should reflect the movement.
   // The reported position is offset from the center of |start_rect|.
   gfx::PointF start_offset = start_rect.CenterPoint();
-  event = MockMotionEvent(MockMotionEvent::ACTION_MOVE, event_time, 0, 5);
+  event = MockMotionEvent(MockMotionEvent::Action::MOVE, event_time, 0, 5);
   EXPECT_TRUE(controller().WillHandleTouchEvent(event));
   EXPECT_TRUE(GetAndResetCaretMoved());
   EXPECT_EQ(start_offset + gfx::Vector2dF(0, 5), GetLastCaretPosition());
 
-  event = MockMotionEvent(MockMotionEvent::ACTION_MOVE, event_time, 5, 5);
+  event = MockMotionEvent(MockMotionEvent::Action::MOVE, event_time, 5, 5);
   EXPECT_TRUE(controller().WillHandleTouchEvent(event));
   EXPECT_TRUE(GetAndResetCaretMoved());
   EXPECT_EQ(start_offset + gfx::Vector2dF(5, 5), GetLastCaretPosition());
 
-  event = MockMotionEvent(MockMotionEvent::ACTION_MOVE, event_time, 10, 10);
+  event = MockMotionEvent(MockMotionEvent::Action::MOVE, event_time, 10, 10);
   EXPECT_TRUE(controller().WillHandleTouchEvent(event));
   EXPECT_TRUE(GetAndResetCaretMoved());
   EXPECT_EQ(start_offset + gfx::Vector2dF(10, 10), GetLastCaretPosition());
 
-  event = MockMotionEvent(MockMotionEvent::ACTION_UP, event_time, 10, 5);
+  event = MockMotionEvent(MockMotionEvent::Action::UP, event_time, 10, 5);
   EXPECT_TRUE(controller().WillHandleTouchEvent(event));
   EXPECT_FALSE(GetAndResetCaretMoved());
   EXPECT_THAT(GetAndResetEvents(), ElementsAre(INSERTION_HANDLE_DRAG_STOPPED));
 
-  // Following ACTION_DOWN should not be consumed if it does not start handle
+  // Following Action::DOWN should not be consumed if it does not start handle
   // dragging.
   SetDraggingEnabled(false);
-  event = MockMotionEvent(MotionEvent::ACTION_DOWN, event_time, 0, 0);
+  event = MockMotionEvent(MotionEvent::Action::DOWN, event_time, 0, 0);
   EXPECT_FALSE(controller().WillHandleTouchEvent(event));
 }
 
@@ -388,18 +388,18 @@
               ElementsAre(INSERTION_HANDLE_SHOWN));
   EXPECT_EQ(start_rect.bottom_left(), GetLastEventStart());
 
-  // Enable dragging so that the following ACTION_DOWN starts handle dragging.
+  // Enable dragging so that the following Action::DOWN starts handle dragging.
   SetDraggingEnabled(true);
 
   // Touch down to start dragging.
-  MockMotionEvent event(MockMotionEvent::ACTION_DOWN, event_time, 0, 0);
+  MockMotionEvent event(MockMotionEvent::Action::DOWN, event_time, 0, 0);
   EXPECT_TRUE(controller().WillHandleTouchEvent(event));
   EXPECT_FALSE(GetAndResetCaretMoved());
   EXPECT_THAT(GetAndResetEvents(), ElementsAre(INSERTION_HANDLE_DRAG_STARTED));
 
   // Move the handle.
   gfx::PointF start_offset = start_rect.CenterPoint();
-  event = MockMotionEvent(MockMotionEvent::ACTION_MOVE, event_time, 0, 5);
+  event = MockMotionEvent(MockMotionEvent::Action::MOVE, event_time, 0, 5);
   EXPECT_TRUE(controller().WillHandleTouchEvent(event));
   EXPECT_TRUE(GetAndResetCaretMoved());
   EXPECT_EQ(start_offset + gfx::Vector2dF(0, 5), GetLastCaretPosition());
@@ -412,21 +412,21 @@
   // Move the finger. There is no handle to move, so the cursor is not moved;
   // but, the event is still consumed because the touch down that started the
   // touch sequence was consumed.
-  event = MockMotionEvent(MockMotionEvent::ACTION_MOVE, event_time, 5, 5);
+  event = MockMotionEvent(MockMotionEvent::Action::MOVE, event_time, 5, 5);
   EXPECT_TRUE(controller().WillHandleTouchEvent(event));
   EXPECT_FALSE(GetAndResetCaretMoved());
   EXPECT_EQ(start_offset + gfx::Vector2dF(0, 5), GetLastCaretPosition());
 
   // Lift the finger to end the touch sequence.
-  event = MockMotionEvent(MockMotionEvent::ACTION_UP, event_time, 5, 5);
+  event = MockMotionEvent(MockMotionEvent::Action::UP, event_time, 5, 5);
   EXPECT_TRUE(controller().WillHandleTouchEvent(event));
   EXPECT_FALSE(GetAndResetCaretMoved());
   EXPECT_THAT(GetAndResetEvents(), IsEmpty());
 
-  // Following ACTION_DOWN should not be consumed if it does not start handle
+  // Following Action::DOWN should not be consumed if it does not start handle
   // dragging.
   SetDraggingEnabled(false);
-  event = MockMotionEvent(MotionEvent::ACTION_DOWN, event_time, 0, 0);
+  event = MockMotionEvent(MotionEvent::Action::DOWN, event_time, 0, 0);
   EXPECT_FALSE(controller().WillHandleTouchEvent(event));
 }
 
@@ -441,11 +441,11 @@
   EXPECT_THAT(GetAndResetEvents(),
               ElementsAre(INSERTION_HANDLE_SHOWN));
 
-  MockMotionEvent event(MockMotionEvent::ACTION_DOWN, event_time, 0, 0);
+  MockMotionEvent event(MockMotionEvent::Action::DOWN, event_time, 0, 0);
   EXPECT_TRUE(controller().WillHandleTouchEvent(event));
   EXPECT_THAT(GetAndResetEvents(), ElementsAre(INSERTION_HANDLE_DRAG_STARTED));
 
-  event = MockMotionEvent(MockMotionEvent::ACTION_UP, event_time, 0, 0);
+  event = MockMotionEvent(MockMotionEvent::Action::UP, event_time, 0, 0);
   EXPECT_TRUE(controller().WillHandleTouchEvent(event));
   EXPECT_THAT(GetAndResetEvents(), ElementsAre(INSERTION_HANDLE_TAPPED,
                                                INSERTION_HANDLE_DRAG_STOPPED));
@@ -458,12 +458,10 @@
               ElementsAre(INSERTION_HANDLE_CLEARED, INSERTION_HANDLE_SHOWN));
 
   // No tap should be signalled if the time between DOWN and UP was too long.
-  event = MockMotionEvent(MockMotionEvent::ACTION_DOWN, event_time, 0, 0);
+  event = MockMotionEvent(MockMotionEvent::Action::DOWN, event_time, 0, 0);
   EXPECT_TRUE(controller().WillHandleTouchEvent(event));
-  event = MockMotionEvent(MockMotionEvent::ACTION_UP,
-                          event_time + base::TimeDelta::FromSeconds(1),
-                          0,
-                          0);
+  event = MockMotionEvent(MockMotionEvent::Action::UP,
+                          event_time + base::TimeDelta::FromSeconds(1), 0, 0);
   EXPECT_TRUE(controller().WillHandleTouchEvent(event));
   EXPECT_THAT(GetAndResetEvents(), ElementsAre(INSERTION_HANDLE_DRAG_STARTED,
                                                INSERTION_HANDLE_DRAG_STOPPED));
@@ -476,11 +474,11 @@
               ElementsAre(INSERTION_HANDLE_CLEARED, INSERTION_HANDLE_SHOWN));
 
   // No tap should be signalled if the drag was too long.
-  event = MockMotionEvent(MockMotionEvent::ACTION_DOWN, event_time, 0, 0);
+  event = MockMotionEvent(MockMotionEvent::Action::DOWN, event_time, 0, 0);
   EXPECT_TRUE(controller().WillHandleTouchEvent(event));
-  event = MockMotionEvent(MockMotionEvent::ACTION_MOVE, event_time, 100, 0);
+  event = MockMotionEvent(MockMotionEvent::Action::MOVE, event_time, 100, 0);
   EXPECT_TRUE(controller().WillHandleTouchEvent(event));
-  event = MockMotionEvent(MockMotionEvent::ACTION_UP, event_time, 100, 0);
+  event = MockMotionEvent(MockMotionEvent::Action::UP, event_time, 100, 0);
   EXPECT_TRUE(controller().WillHandleTouchEvent(event));
   EXPECT_THAT(GetAndResetEvents(), ElementsAre(INSERTION_HANDLE_DRAG_STARTED,
                                                INSERTION_HANDLE_DRAG_STOPPED));
@@ -493,9 +491,9 @@
               ElementsAre(INSERTION_HANDLE_CLEARED, INSERTION_HANDLE_SHOWN));
 
   // No tap should be signalled if the touch sequence is cancelled.
-  event = MockMotionEvent(MockMotionEvent::ACTION_DOWN, event_time, 0, 0);
+  event = MockMotionEvent(MockMotionEvent::Action::DOWN, event_time, 0, 0);
   EXPECT_TRUE(controller().WillHandleTouchEvent(event));
-  event = MockMotionEvent(MockMotionEvent::ACTION_CANCEL, event_time, 0, 0);
+  event = MockMotionEvent(MockMotionEvent::Action::CANCEL, event_time, 0, 0);
   EXPECT_TRUE(controller().WillHandleTouchEvent(event));
   EXPECT_THAT(GetAndResetEvents(), ElementsAre(INSERTION_HANDLE_DRAG_STARTED,
                                                INSERTION_HANDLE_DRAG_STOPPED));
@@ -631,7 +629,7 @@
   OnLongPressEvent();
 
   // The touch sequence should not be handled if selection is not active.
-  MockMotionEvent event(MockMotionEvent::ACTION_DOWN, event_time, 0, 0);
+  MockMotionEvent event(MockMotionEvent::Action::DOWN, event_time, 0, 0);
   EXPECT_FALSE(controller().WillHandleTouchEvent(event));
 
   float line_height = 10.f;
@@ -656,34 +654,34 @@
   // input rects (i.e., the middle of the corresponding text line).
   gfx::PointF fixed_offset = end_rect.CenterPoint();
   gfx::PointF start_offset = start_rect.CenterPoint();
-  event = MockMotionEvent(MockMotionEvent::ACTION_MOVE, event_time, 0, 5);
+  event = MockMotionEvent(MockMotionEvent::Action::MOVE, event_time, 0, 5);
   EXPECT_TRUE(controller().WillHandleTouchEvent(event));
   EXPECT_THAT(GetAndResetEvents(), ElementsAre(SELECTION_HANDLE_DRAG_STARTED));
   EXPECT_TRUE(GetAndResetSelectionMoved());
   EXPECT_EQ(fixed_offset, GetLastSelectionStart());
   EXPECT_EQ(start_offset + gfx::Vector2dF(0, 5), GetLastSelectionEnd());
 
-  event = MockMotionEvent(MockMotionEvent::ACTION_MOVE, event_time, 5, 5);
+  event = MockMotionEvent(MockMotionEvent::Action::MOVE, event_time, 5, 5);
   EXPECT_TRUE(controller().WillHandleTouchEvent(event));
   EXPECT_TRUE(GetAndResetSelectionMoved());
   EXPECT_EQ(fixed_offset, GetLastSelectionStart());
   EXPECT_EQ(start_offset + gfx::Vector2dF(5, 5), GetLastSelectionEnd());
 
-  event = MockMotionEvent(MockMotionEvent::ACTION_MOVE, event_time, 10, 5);
+  event = MockMotionEvent(MockMotionEvent::Action::MOVE, event_time, 10, 5);
   EXPECT_TRUE(controller().WillHandleTouchEvent(event));
   EXPECT_TRUE(GetAndResetSelectionMoved());
   EXPECT_EQ(fixed_offset, GetLastSelectionStart());
   EXPECT_EQ(start_offset + gfx::Vector2dF(10, 5), GetLastSelectionEnd());
 
-  event = MockMotionEvent(MockMotionEvent::ACTION_UP, event_time, 10, 5);
+  event = MockMotionEvent(MockMotionEvent::Action::UP, event_time, 10, 5);
   EXPECT_TRUE(controller().WillHandleTouchEvent(event));
   EXPECT_THAT(GetAndResetEvents(), ElementsAre(SELECTION_HANDLE_DRAG_STOPPED));
   EXPECT_FALSE(GetAndResetSelectionMoved());
 
-  // Following ACTION_DOWN should not be consumed if it does not start handle
+  // Following Action::DOWN should not be consumed if it does not start handle
   // dragging.
   SetDraggingEnabled(false);
-  event = MockMotionEvent(MotionEvent::ACTION_DOWN, event_time, 0, 0);
+  event = MockMotionEvent(MotionEvent::Action::DOWN, event_time, 0, 0);
   EXPECT_FALSE(controller().WillHandleTouchEvent(event));
 }
 
@@ -700,27 +698,27 @@
               ElementsAre(SELECTION_HANDLES_SHOWN));
   EXPECT_EQ(start_rect.bottom_left(), GetLastEventStart());
 
-  // The ACTION_DOWN should lock to the closest handle.
+  // The Action::DOWN should lock to the closest handle.
   gfx::PointF end_offset = end_rect.CenterPoint();
   gfx::PointF fixed_offset = start_rect.CenterPoint();
   float touch_down_x = (end_offset.x() + fixed_offset.x()) / 2 + 1.f;
-  MockMotionEvent event(
-      MockMotionEvent::ACTION_DOWN, event_time, touch_down_x, 0);
+  MockMotionEvent event(MockMotionEvent::Action::DOWN, event_time, touch_down_x,
+                        0);
   SetDraggingEnabled(true);
   EXPECT_TRUE(controller().WillHandleTouchEvent(event));
   EXPECT_THAT(GetAndResetEvents(), ElementsAre(SELECTION_HANDLE_DRAG_STARTED));
   EXPECT_FALSE(GetAndResetSelectionMoved());
 
-  // Even though the ACTION_MOVE is over the start handle, it should continue
-  // targetting the end handle that consumed the ACTION_DOWN.
-  event = MockMotionEvent(MockMotionEvent::ACTION_MOVE, event_time, 0, 0);
+  // Even though the Action::MOVE is over the start handle, it should continue
+  // targetting the end handle that consumed the Action::DOWN.
+  event = MockMotionEvent(MockMotionEvent::Action::MOVE, event_time, 0, 0);
   EXPECT_TRUE(controller().WillHandleTouchEvent(event));
   EXPECT_TRUE(GetAndResetSelectionMoved());
   EXPECT_EQ(fixed_offset, GetLastSelectionStart());
   EXPECT_EQ(end_offset - gfx::Vector2dF(touch_down_x, 0),
             GetLastSelectionEnd());
 
-  event = MockMotionEvent(MockMotionEvent::ACTION_UP, event_time, 0, 0);
+  event = MockMotionEvent(MockMotionEvent::Action::UP, event_time, 0, 0);
   EXPECT_TRUE(controller().WillHandleTouchEvent(event));
   EXPECT_THAT(GetAndResetEvents(), ElementsAre(SELECTION_HANDLE_DRAG_STOPPED));
   EXPECT_FALSE(GetAndResetSelectionMoved());
@@ -742,15 +740,15 @@
   SetDraggingEnabled(true);
 
   // Move the extent, not triggering a swap of points.
-  MockMotionEvent event(MockMotionEvent::ACTION_DOWN, event_time,
-                        end_rect.x(), end_rect.bottom());
+  MockMotionEvent event(MockMotionEvent::Action::DOWN, event_time, end_rect.x(),
+                        end_rect.bottom());
   EXPECT_TRUE(controller().WillHandleTouchEvent(event));
   EXPECT_FALSE(GetAndResetSelectionMoved());
   EXPECT_FALSE(GetAndResetSelectionPointsSwapped());
 
   gfx::PointF base_offset = start_rect.CenterPoint();
   gfx::PointF extent_offset = end_rect.CenterPoint();
-  event = MockMotionEvent(MockMotionEvent::ACTION_MOVE, event_time,
+  event = MockMotionEvent(MockMotionEvent::Action::MOVE, event_time,
                           end_rect.x(), end_rect.bottom() + 5);
   EXPECT_TRUE(controller().WillHandleTouchEvent(event));
   EXPECT_THAT(GetAndResetEvents(), ElementsAre(SELECTION_HANDLE_DRAG_STARTED));
@@ -759,7 +757,7 @@
   EXPECT_EQ(base_offset, GetLastSelectionStart());
   EXPECT_EQ(extent_offset + gfx::Vector2dF(0, 5), GetLastSelectionEnd());
 
-  event = MockMotionEvent(MockMotionEvent::ACTION_UP, event_time, 10, 5);
+  event = MockMotionEvent(MockMotionEvent::Action::UP, event_time, 10, 5);
   EXPECT_TRUE(controller().WillHandleTouchEvent(event));
   EXPECT_THAT(GetAndResetEvents(), ElementsAre(SELECTION_HANDLE_DRAG_STOPPED));
   EXPECT_FALSE(GetAndResetSelectionMoved());
@@ -769,7 +767,7 @@
   EXPECT_THAT(GetAndResetEvents(), ElementsAre(SELECTION_HANDLES_MOVED));
 
   // Move the base, triggering a swap of points.
-  event = MockMotionEvent(MockMotionEvent::ACTION_DOWN, event_time,
+  event = MockMotionEvent(MockMotionEvent::Action::DOWN, event_time,
                           start_rect.x(), start_rect.bottom());
   EXPECT_TRUE(controller().WillHandleTouchEvent(event));
   EXPECT_FALSE(GetAndResetSelectionMoved());
@@ -777,7 +775,7 @@
 
   base_offset = end_rect.CenterPoint();
   extent_offset = start_rect.CenterPoint();
-  event = MockMotionEvent(MockMotionEvent::ACTION_MOVE, event_time,
+  event = MockMotionEvent(MockMotionEvent::Action::MOVE, event_time,
                           start_rect.x(), start_rect.bottom() + 5);
   EXPECT_TRUE(controller().WillHandleTouchEvent(event));
   EXPECT_THAT(GetAndResetEvents(), ElementsAre(SELECTION_HANDLE_DRAG_STARTED));
@@ -786,7 +784,7 @@
   EXPECT_EQ(base_offset, GetLastSelectionStart());
   EXPECT_EQ(extent_offset + gfx::Vector2dF(0, 5), GetLastSelectionEnd());
 
-  event = MockMotionEvent(MockMotionEvent::ACTION_UP, event_time, 10, 5);
+  event = MockMotionEvent(MockMotionEvent::Action::UP, event_time, 10, 5);
   EXPECT_TRUE(controller().WillHandleTouchEvent(event));
   EXPECT_THAT(GetAndResetEvents(), ElementsAre(SELECTION_HANDLE_DRAG_STOPPED));
   EXPECT_FALSE(GetAndResetSelectionMoved());
@@ -796,7 +794,7 @@
   EXPECT_THAT(GetAndResetEvents(), ElementsAre(SELECTION_HANDLES_MOVED));
 
   // Move the same point again, not triggering a swap of points.
-  event = MockMotionEvent(MockMotionEvent::ACTION_DOWN, event_time,
+  event = MockMotionEvent(MockMotionEvent::Action::DOWN, event_time,
                           start_rect.x(), start_rect.bottom());
   EXPECT_TRUE(controller().WillHandleTouchEvent(event));
   EXPECT_FALSE(GetAndResetSelectionMoved());
@@ -804,7 +802,7 @@
 
   base_offset = end_rect.CenterPoint();
   extent_offset = start_rect.CenterPoint();
-  event = MockMotionEvent(MockMotionEvent::ACTION_MOVE, event_time,
+  event = MockMotionEvent(MockMotionEvent::Action::MOVE, event_time,
                           start_rect.x(), start_rect.bottom() + 5);
   EXPECT_TRUE(controller().WillHandleTouchEvent(event));
   EXPECT_THAT(GetAndResetEvents(), ElementsAre(SELECTION_HANDLE_DRAG_STARTED));
@@ -813,7 +811,7 @@
   EXPECT_EQ(base_offset, GetLastSelectionStart());
   EXPECT_EQ(extent_offset + gfx::Vector2dF(0, 5), GetLastSelectionEnd());
 
-  event = MockMotionEvent(MockMotionEvent::ACTION_UP, event_time, 10, 5);
+  event = MockMotionEvent(MockMotionEvent::Action::UP, event_time, 10, 5);
   EXPECT_TRUE(controller().WillHandleTouchEvent(event));
   EXPECT_THAT(GetAndResetEvents(), ElementsAre(SELECTION_HANDLE_DRAG_STOPPED));
   EXPECT_FALSE(GetAndResetSelectionMoved());
@@ -823,7 +821,7 @@
   EXPECT_THAT(GetAndResetEvents(), ElementsAre(SELECTION_HANDLES_MOVED));
 
   // Move the base, triggering a swap of points.
-  event = MockMotionEvent(MockMotionEvent::ACTION_DOWN, event_time,
+  event = MockMotionEvent(MockMotionEvent::Action::DOWN, event_time,
                           end_rect.x(), end_rect.bottom());
   EXPECT_TRUE(controller().WillHandleTouchEvent(event));
   EXPECT_FALSE(GetAndResetSelectionMoved());
@@ -831,7 +829,7 @@
 
   base_offset = start_rect.CenterPoint();
   extent_offset = end_rect.CenterPoint();
-  event = MockMotionEvent(MockMotionEvent::ACTION_MOVE, event_time,
+  event = MockMotionEvent(MockMotionEvent::Action::MOVE, event_time,
                           end_rect.x(), end_rect.bottom() + 5);
   EXPECT_TRUE(controller().WillHandleTouchEvent(event));
   EXPECT_THAT(GetAndResetEvents(), ElementsAre(SELECTION_HANDLE_DRAG_STARTED));
@@ -840,7 +838,7 @@
   EXPECT_EQ(base_offset, GetLastSelectionStart());
   EXPECT_EQ(extent_offset + gfx::Vector2dF(0, 5), GetLastSelectionEnd());
 
-  event = MockMotionEvent(MockMotionEvent::ACTION_UP, event_time, 10, 5);
+  event = MockMotionEvent(MockMotionEvent::Action::UP, event_time, 10, 5);
   EXPECT_TRUE(controller().WillHandleTouchEvent(event));
   EXPECT_THAT(GetAndResetEvents(), ElementsAre(SELECTION_HANDLE_DRAG_STOPPED));
   EXPECT_FALSE(GetAndResetSelectionMoved());
@@ -861,7 +859,7 @@
   EXPECT_EQ(small_line_rect.bottom_left(), GetLastEventStart());
 
   // Start dragging the handle on the small line.
-  MockMotionEvent event(MockMotionEvent::ACTION_DOWN, event_time,
+  MockMotionEvent event(MockMotionEvent::Action::DOWN, event_time,
                         small_line_rect.x(), small_line_rect.y());
   SetDraggingEnabled(true);
   EXPECT_TRUE(controller().WillHandleTouchEvent(event));
@@ -875,7 +873,7 @@
   EXPECT_EQ(small_line_rect.CenterPoint(), GetLastSelectionEnd());
 
   small_line_rect += gfx::Vector2dF(25.f, 0);
-  event = MockMotionEvent(MockMotionEvent::ACTION_MOVE, event_time,
+  event = MockMotionEvent(MockMotionEvent::Action::MOVE, event_time,
                           small_line_rect.x(), small_line_rect.y());
   EXPECT_TRUE(controller().WillHandleTouchEvent(event));
   EXPECT_TRUE(GetAndResetSelectionMoved());
@@ -1186,7 +1184,7 @@
   SetDraggingEnabled(true);
 
   // Simulate moving the base, not triggering a swap of points.
-  MockMotionEvent event(MockMotionEvent::ACTION_DOWN, event_time,
+  MockMotionEvent event(MockMotionEvent::Action::DOWN, event_time,
                         start_rect.x(), start_rect.bottom());
   EXPECT_TRUE(controller().WillHandleTouchEvent(event));
   EXPECT_THAT(GetAndResetEvents(), ElementsAre(SELECTION_HANDLE_DRAG_STARTED));
@@ -1201,7 +1199,7 @@
             TouchHandleOrientation::RIGHT);
 
   event_time += base::TimeDelta::FromMilliseconds(2 * kDefaultTapTimeoutMs);
-  event = MockMotionEvent(MockMotionEvent::ACTION_UP, event_time,
+  event = MockMotionEvent(MockMotionEvent::Action::UP, event_time,
                           offset_rect.x(), offset_rect.bottom());
   EXPECT_TRUE(controller().WillHandleTouchEvent(event));
   EXPECT_THAT(GetAndResetEvents(), ElementsAre(SELECTION_HANDLE_DRAG_STOPPED));
@@ -1211,7 +1209,7 @@
             TouchHandleOrientation::RIGHT);
 
   // Simulate moving the base, triggering a swap of points.
-  event = MockMotionEvent(MockMotionEvent::ACTION_DOWN, event_time,
+  event = MockMotionEvent(MockMotionEvent::Action::DOWN, event_time,
                           offset_rect.x(), offset_rect.bottom());
   EXPECT_TRUE(controller().WillHandleTouchEvent(event));
   EXPECT_THAT(GetAndResetEvents(), ElementsAre(SELECTION_HANDLE_DRAG_STARTED));
@@ -1225,7 +1223,7 @@
             TouchHandleOrientation::LEFT);
 
   event_time += base::TimeDelta::FromMilliseconds(2 * kDefaultTapTimeoutMs);
-  event = MockMotionEvent(MockMotionEvent::ACTION_UP, event_time,
+  event = MockMotionEvent(MockMotionEvent::Action::UP, event_time,
                           offset_rect.x(), offset_rect.bottom());
   EXPECT_TRUE(controller().WillHandleTouchEvent(event));
   EXPECT_THAT(GetAndResetEvents(), ElementsAre(SELECTION_HANDLE_DRAG_STOPPED));
@@ -1235,7 +1233,7 @@
             TouchHandleOrientation::RIGHT);
 
   // Simulate moving the anchor, not triggering a swap of points.
-  event = MockMotionEvent(MockMotionEvent::ACTION_DOWN, event_time,
+  event = MockMotionEvent(MockMotionEvent::Action::DOWN, event_time,
                           offset_rect.x(), offset_rect.bottom());
   EXPECT_TRUE(controller().WillHandleTouchEvent(event));
   EXPECT_THAT(GetAndResetEvents(), ElementsAre(SELECTION_HANDLE_DRAG_STARTED));
@@ -1249,7 +1247,7 @@
             TouchHandleOrientation::RIGHT);
 
   event_time += base::TimeDelta::FromMilliseconds(2 * kDefaultTapTimeoutMs);
-  event = MockMotionEvent(MockMotionEvent::ACTION_UP, event_time,
+  event = MockMotionEvent(MockMotionEvent::Action::UP, event_time,
                           offset_rect.x(), offset_rect.bottom());
   EXPECT_TRUE(controller().WillHandleTouchEvent(event));
   EXPECT_THAT(GetAndResetEvents(), ElementsAre(SELECTION_HANDLE_DRAG_STOPPED));
@@ -1259,7 +1257,7 @@
             TouchHandleOrientation::RIGHT);
 
   // Simulate moving the anchor, triggering a swap of points.
-  event = MockMotionEvent(MockMotionEvent::ACTION_DOWN, event_time,
+  event = MockMotionEvent(MockMotionEvent::Action::DOWN, event_time,
                           offset_rect.x(), offset_rect.bottom());
   EXPECT_TRUE(controller().WillHandleTouchEvent(event));
   EXPECT_THAT(GetAndResetEvents(), ElementsAre(SELECTION_HANDLE_DRAG_STARTED));
@@ -1273,7 +1271,7 @@
             TouchHandleOrientation::RIGHT);
 
   event_time += base::TimeDelta::FromMilliseconds(2 * kDefaultTapTimeoutMs);
-  event = MockMotionEvent(MockMotionEvent::ACTION_UP, event_time,
+  event = MockMotionEvent(MockMotionEvent::Action::UP, event_time,
                           offset_rect.x(), offset_rect.bottom());
   EXPECT_TRUE(controller().WillHandleTouchEvent(event));
   EXPECT_THAT(GetAndResetEvents(), ElementsAre(SELECTION_HANDLE_DRAG_STOPPED));
@@ -1305,7 +1303,7 @@
 
   // Simulate moving the base, triggering a swap of points.
   // Start to drag start handle.
-  MockMotionEvent event(MockMotionEvent::ACTION_DOWN, event_time,
+  MockMotionEvent event(MockMotionEvent::Action::DOWN, event_time,
                         start_rect.right(), start_rect.bottom());
   EXPECT_TRUE(controller().WillHandleTouchEvent(event));
   EXPECT_THAT(GetAndResetEvents(), ElementsAre(SELECTION_HANDLE_DRAG_STARTED));
@@ -1322,7 +1320,7 @@
 
   // Release.
   event_time += base::TimeDelta::FromMilliseconds(2 * kDefaultTapTimeoutMs);
-  event = MockMotionEvent(MockMotionEvent::ACTION_UP, event_time,
+  event = MockMotionEvent(MockMotionEvent::Action::UP, event_time,
                           offset_rect.x(), offset_rect.bottom());
   EXPECT_TRUE(controller().WillHandleTouchEvent(event));
   EXPECT_THAT(GetAndResetEvents(), ElementsAre(SELECTION_HANDLE_DRAG_STOPPED));
@@ -1333,7 +1331,7 @@
 
   // Move end handle up.
   // Start to drag end handle.
-  event = MockMotionEvent(MockMotionEvent::ACTION_DOWN, event_time,
+  event = MockMotionEvent(MockMotionEvent::Action::DOWN, event_time,
                           offset_rect.x(), offset_rect.bottom());
   EXPECT_TRUE(controller().WillHandleTouchEvent(event));
   EXPECT_THAT(GetAndResetEvents(), ElementsAre(SELECTION_HANDLE_DRAG_STARTED));
@@ -1349,7 +1347,7 @@
 
   // Release.
   event_time += base::TimeDelta::FromMilliseconds(2 * kDefaultTapTimeoutMs);
-  event = MockMotionEvent(MockMotionEvent::ACTION_UP, event_time,
+  event = MockMotionEvent(MockMotionEvent::Action::UP, event_time,
                           offset_rect.x(), offset_rect.bottom());
   EXPECT_TRUE(controller().WillHandleTouchEvent(event));
   EXPECT_THAT(GetAndResetEvents(), ElementsAre(SELECTION_HANDLE_DRAG_STOPPED));
@@ -1371,33 +1369,33 @@
   EXPECT_EQ(gfx::PointF(0.f, 0.f), GetLastDragUpdatePosition());
 
   SetDraggingEnabled(true);
-  MockMotionEvent event(MockMotionEvent::ACTION_DOWN, event_time, 10, 5);
+  MockMotionEvent event(MockMotionEvent::Action::DOWN, event_time, 10, 5);
   EXPECT_TRUE(controller().WillHandleTouchEvent(event));
-  event = MockMotionEvent(MockMotionEvent::ACTION_MOVE, event_time, 10, 5);
+  event = MockMotionEvent(MockMotionEvent::Action::MOVE, event_time, 10, 5);
   EXPECT_TRUE(controller().WillHandleTouchEvent(event));
   EXPECT_THAT(GetAndResetEvents(), ElementsAre(INSERTION_HANDLE_DRAG_STARTED));
   EXPECT_EQ(gfx::PointF(10.f, 5.f), GetLastDragUpdatePosition());
 
   insertion_rect.Offset(1, 0);
   ChangeInsertion(insertion_rect, visible);
-  event = MockMotionEvent(MockMotionEvent::ACTION_MOVE, event_time, 12, 6);
+  event = MockMotionEvent(MockMotionEvent::Action::MOVE, event_time, 12, 6);
   EXPECT_TRUE(controller().WillHandleTouchEvent(event));
   EXPECT_THAT(GetAndResetEvents(), ElementsAre(INSERTION_HANDLE_MOVED));
   // Don't follow the y-coordinate change but only x-coordinate change.
   EXPECT_EQ(gfx::PointF(12.f, 5.f), GetLastDragUpdatePosition());
 
   insertion_rect.Offset(0, 1);
-  event = MockMotionEvent(MockMotionEvent::ACTION_MOVE, event_time, 11, 6);
+  event = MockMotionEvent(MockMotionEvent::Action::MOVE, event_time, 11, 6);
   EXPECT_TRUE(controller().WillHandleTouchEvent(event));
   ChangeInsertion(insertion_rect, visible);
-  event = MockMotionEvent(MockMotionEvent::ACTION_MOVE, event_time, 11, 7);
+  event = MockMotionEvent(MockMotionEvent::Action::MOVE, event_time, 11, 7);
   EXPECT_TRUE(controller().WillHandleTouchEvent(event));
   EXPECT_THAT(GetAndResetEvents(), ElementsAre(INSERTION_HANDLE_MOVED));
   // Don't follow the y-coordinate change.
   EXPECT_EQ(gfx::PointF(11.f, 6.f), GetLastDragUpdatePosition());
 
   event_time += base::TimeDelta::FromMilliseconds(2 * kDefaultTapTimeoutMs);
-  event = MockMotionEvent(MockMotionEvent::ACTION_UP, event_time, 0, 0);
+  event = MockMotionEvent(MockMotionEvent::Action::UP, event_time, 0, 0);
   EXPECT_TRUE(controller().WillHandleTouchEvent(event));
   EXPECT_THAT(GetAndResetEvents(), ElementsAre(INSERTION_HANDLE_DRAG_STOPPED));
 
@@ -1418,14 +1416,14 @@
 
   // Left handle.
   SetDraggingEnabled(true);
-  MockMotionEvent event(MockMotionEvent::ACTION_DOWN, event_time, 10, 5);
+  MockMotionEvent event(MockMotionEvent::Action::DOWN, event_time, 10, 5);
   EXPECT_TRUE(controller().WillHandleTouchEvent(event));
-  event = MockMotionEvent(MockMotionEvent::ACTION_MOVE, event_time, 10, 5);
+  event = MockMotionEvent(MockMotionEvent::Action::MOVE, event_time, 10, 5);
   EXPECT_TRUE(controller().WillHandleTouchEvent(event));
   EXPECT_THAT(GetAndResetEvents(), ElementsAre(SELECTION_HANDLE_DRAG_STARTED));
   EXPECT_EQ(gfx::PointF(10.f, 5.f), GetLastDragUpdatePosition());
 
-  event = MockMotionEvent(MockMotionEvent::ACTION_MOVE, event_time, 16, 6);
+  event = MockMotionEvent(MockMotionEvent::Action::MOVE, event_time, 16, 6);
   start_rect.Offset(5, 0);
   ChangeSelection(start_rect, visible, end_rect, visible);
   EXPECT_TRUE(controller().WillHandleTouchEvent(event));
@@ -1434,19 +1432,19 @@
   EXPECT_EQ(gfx::PointF(16.f, 5.f), GetLastDragUpdatePosition());
 
   event_time += base::TimeDelta::FromMilliseconds(2 * kDefaultTapTimeoutMs);
-  event = MockMotionEvent(MockMotionEvent::ACTION_UP, event_time, 15, 5);
+  event = MockMotionEvent(MockMotionEvent::Action::UP, event_time, 15, 5);
   EXPECT_TRUE(controller().WillHandleTouchEvent(event));
   EXPECT_THAT(GetAndResetEvents(), ElementsAre(SELECTION_HANDLE_DRAG_STOPPED));
 
   // Right handle.
-  event = MockMotionEvent(MockMotionEvent::ACTION_DOWN, event_time, 50, 5);
+  event = MockMotionEvent(MockMotionEvent::Action::DOWN, event_time, 50, 5);
   EXPECT_TRUE(controller().WillHandleTouchEvent(event));
-  event = MockMotionEvent(MockMotionEvent::ACTION_MOVE, event_time, 50, 5);
+  event = MockMotionEvent(MockMotionEvent::Action::MOVE, event_time, 50, 5);
   EXPECT_TRUE(controller().WillHandleTouchEvent(event));
   EXPECT_THAT(GetAndResetEvents(), ElementsAre(SELECTION_HANDLE_DRAG_STARTED));
   EXPECT_EQ(gfx::PointF(50.f, 5.f), GetLastDragUpdatePosition());
 
-  event = MockMotionEvent(MockMotionEvent::ACTION_MOVE, event_time, 45, 5);
+  event = MockMotionEvent(MockMotionEvent::Action::MOVE, event_time, 45, 5);
   end_rect.Offset(-5, 0);
   ChangeSelection(start_rect, visible, end_rect, visible);
   EXPECT_TRUE(controller().WillHandleTouchEvent(event));
@@ -1454,7 +1452,7 @@
   EXPECT_EQ(gfx::PointF(45.f, 5.f), GetLastDragUpdatePosition());
 
   event_time += base::TimeDelta::FromMilliseconds(2 * kDefaultTapTimeoutMs);
-  event = MockMotionEvent(MockMotionEvent::ACTION_UP, event_time, 45, 5);
+  event = MockMotionEvent(MockMotionEvent::Action::UP, event_time, 45, 5);
   EXPECT_TRUE(controller().WillHandleTouchEvent(event));
   EXPECT_THAT(GetAndResetEvents(), ElementsAre(SELECTION_HANDLE_DRAG_STOPPED));
 }
diff --git a/ui/views/mus/aura_init.cc b/ui/views/mus/aura_init.cc
index ac7aa35..7631ab5 100644
--- a/ui/views/mus/aura_init.cc
+++ b/ui/views/mus/aura_init.cc
@@ -133,7 +133,7 @@
                                    const std::string& resource_file,
                                    const std::string& resource_file_200,
                                    bool register_path_provider) {
-  // Resources may have already been initialized (e.g. when 'chrome --mash' is
+  // Resources may have already been initialized (e.g. when chrome with mash is
   // used to launch the current app).
   if (ui::ResourceBundle::HasSharedInstance())
     return true;
diff --git a/ui/views/mus/views_mus_test_suite.cc b/ui/views/mus/views_mus_test_suite.cc
index 202bd8c9..0008b8e 100644
--- a/ui/views/mus/views_mus_test_suite.cc
+++ b/ui/views/mus/views_mus_test_suite.cc
@@ -7,6 +7,7 @@
 #include <memory>
 #include <string>
 
+#include "base/base_switches.h"
 #include "base/command_line.h"
 #include "base/files/file_path.h"
 #include "base/memory/ptr_util.h"
@@ -28,6 +29,7 @@
 #include "ui/aura/mus/window_tree_host_mus.h"
 #include "ui/aura/test/mus/input_method_mus_test_api.h"
 #include "ui/aura/window.h"
+#include "ui/base/ui_base_features.h"
 #include "ui/base/ui_base_switches.h"
 #include "ui/compositor/test/fake_context_factory.h"
 #include "ui/gl/gl_switches.h"
@@ -240,12 +242,15 @@
   EnsureCommandLineSwitch(ui::switches::kUseTestConfig);
 
   EnsureCommandLineSwitch(switches::kOverrideUseSoftwareGLForTests);
-  base::CommandLine::ForCurrentProcess()->AppendSwitch(switches::kMus);
-  base::CommandLine::ForCurrentProcess()->AppendSwitch(
-      switches::kMusHostingViz);
 
   ViewsTestSuite::Initialize();
 
+  // NOTE: this has to be after ViewsTestSuite::Initialize() as
+  // TestSuite::Initialize() resets kEnableFeatures and the command line.
+  feature_list_.InitAndEnableFeature(features::kMash);
+  base::CommandLine::ForCurrentProcess()->AppendSwitchASCII(
+      switches::kEnableFeatures, features::kMash.name);
+
   PlatformTestHelper::set_factory(base::Bind(&CreatePlatformTestHelper));
 }
 
diff --git a/ui/views/mus/views_mus_test_suite.h b/ui/views/mus/views_mus_test_suite.h
index d8184d0e..4dc9d09 100644
--- a/ui/views/mus/views_mus_test_suite.h
+++ b/ui/views/mus/views_mus_test_suite.h
@@ -8,6 +8,7 @@
 #include <memory>
 
 #include "base/macros.h"
+#include "base/test/scoped_feature_list.h"
 #include "ui/views/views_test_suite.h"
 
 namespace views {
@@ -23,6 +24,8 @@
   void InitializeEnv() override;
   void DestroyEnv() override;
 
+  base::test::ScopedFeatureList feature_list_;
+
   std::unique_ptr<aura::Env> env_;
 
   DISALLOW_COPY_AND_ASSIGN(ViewsMusTestSuite);